[iOS] UIView를 UIImage로 변환 (CGContext)

2021. 10. 23. 18:57iOS

반응형

오늘은 UIView를 UIImage로 변환하는 방법에 대해 알아보려고 하는데요 :)

 

이번에 진행하는 프로젝트에서 UIImage 관련 설정을 만지다보니 조금 생소한 인터페이스들을 만나게 되었고 우선 구현은 했지만 조금은 어색하게 느껴져 공부해보면서 어떤 인터페이스인지 알아보겠습니다. 

 

우선 UIView -> UIImage로 변환하는 작업에서 해당 인터페이스를 사용하기에 간단한 코드를 보면서 어떤 역할을 하는 코드들인지 알아보겠습니다. (메인은 각각 어떤 동작을 하는지 ㅎㅎ, 사실상 포스팅 제목은 페이크)

 

UIView to UIImage

우선 UIView를 UImage로 렌더링을 하는 코드를 먼저 보고 하나씩 어떤 역할을 하는지 설명하면서 알아보겠습니다.

extension UIView {
    
    func asImage() -> UIImage? {
        UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0)
        defer { UIGraphicsEndImageContext() }
        
        guard let currentContext = UIGraphicsGetCurrentContext() else { return nil }
        self.layer.render(in: currentContext)
        return UIGraphicsGetImageFromCurrentImageContext()
    }
    
}

우선 코드는 엄청 간단한데요. 조금 생소하지 않나요..?

저는 그동안 사용하지 않는 메소드들이라 조금 많이 생소했는데요. 

 

한번 각각 한줄씩 어떻게 사용하는지 알아볼게요.

  • UIGraphicsBeginImageContextWithOptions(_: _: _:) : 애플 문서의 설명을 보면 bitmap 기반의 Graphic Context를 생성한다고 되어 있습니다. 여기서 3개의 파라메터를 받는데요. 어떤 옵션을 이용해서 Context를 생성할건지를 의미합니다. 

    여기서 중요한 점은 이 메소드를 호출 한 후에 Context 기반의 이미지를 UIGraphicsGetImageFromCurrentImageContext()로 얻어올 수 있습니다. 또한 이 메소드를 호출했다면 꼭 작업이 끝난 후, UIGraphicsEndImageContext()를 호출해서 bitmap 기반의 Context를 Context 스택에서 제거해주어야 합니다.
    • size : 추출될 UIImage의 사이즈를 지정할 수 있는 파라메터
    • opaque : bitmap이 불투명한지를 묻는 Bool 파라메터
    • scale : Scale Factor로 활용되는 값, 0으로 설정하면 UIScreen.main.scale로 자동 설정됩니다.

 

  • UIGraphicsEndImageContext() : 아까 위에서 간단하게 설명했는데, bitmap 기반의 Context를 Context Stack에서 제거해주는 메소드입니다. 만약 UIGraphicsBeginImageContext 메소드가 호출되지 않았을 때 호출해도 아무것도 하지 않으니 문제가 되지 않는다고 합니다.

  • UIGraphicsGetCurrentContext() : 현재의 Context를 가져올 수 있습니다. 아무렇지 않은 상황에서 호출하면 nil 값이 온다고 설명되어 있는데요. 지금 코드 구문에서는 UIGraphicsBeginImageContextWithOptions를 호출했기 때문에, nil이 오지 않고 현재 Context Stack에 들어간 Context를 가져올 수 있습니다.

    또한 이 메소드를 이용해 Context를 가져올 수 있는 순간이 하나 더 있습니다. 바로 UIView.draw(_:)가 불리는 시점인데요. 이 때는 UIView을 렌더링 하는 시점이기 때문에, 시스템에서 유효한 Context를 스택에 push하게 되어서 이 draw(_:)가 불리는 시점에는 접근해서 Context를 가져올 수 있습니다 ㅎㅎ

  • render(in:) : CALayer에 있는 메소드인데요. 현재 레이어와 subLayer들을 매개변수로 주어진 Context에 렌더링하는 메소드입니다. 현재 Layer를 먼저 렌더링 한 다음 UIImage를 얻어오기 위한 메소드 입니다.

  • UIGraphicsGetImageFromCurrentImageContext() : 이제 마지막 메소드인데요 🙂. 지금까지 Context를 생성하고 그 위에 렌더링을 하는 과정을 거쳤습니다. 여기서 이제 현재 Context를 UIImage로 반환해주게 됩니다~~ 당연히 현재 Context 스택에 Context가 없다면 nil이 리턴됩니다. 

 

이렇게 UIView -> UIImage를 반환하는 과정에서 사용했던 메소드들을 각각 알아보았습니다. 그래도 어떤 느낌으로 실행하고 가져오는지 약간은 감이 오지않나요. 메소드들이 조금 생소해서 처음엔 어려웠는데, 알아보니 어떤 느낌인줄 알 것 같았습니다 😂

 

그리고 끝내기 전 마지막으로 iOS 10.0 이상의 버전에서 사용할 수 있는 방법도 알아보겠습니다.

extension UIView {

    func asImage() -> UIImage? {
        let renderer = UIGraphicsImageRenderer(bounds: self.bounds)
        return renderer.image { renderImageContext in
            self.layer.render(in: renderImageContext.cgContext)
        }
    }
        
}

좀 더, 간단하지 않나요..?! 기본 동작은 똑같구요. 사용할 수 있는 버전만 다른 것입니다.

 

UIGraphicsImageRenderer가 10.0 이상부터 사용할 수 있는 객체입니다. 이 객체를 사용하면 위에서 수행했던 Context를 설정하고 세팅하는 동작을 하지 않고도 이미지를 생성할 수 있다고 설명되어 있습니다 ㅎㅎ...

 

실제로도 간단하죠 렌더링 객체를 생성하고 이용해서 이미지를 렌더링해서 리턴해주기만하면 됩니다.

만약 iOS 10.0 미만의 버전도 앱에서 지원이 된다고하면 #available(iOS 10.0, *)을 이용해서 분기해서 사용하면 될 것 같네요.

 

CGContext를 이용하면 Image에 inset을 주는 등, 다양한 작업을 할 수 있을 것 같은데요. 위의 메소들을 알아두면 나중에 활용하기 조금은 수월할 것 같다고 생각이 드네요 🙃

 

조금 간단하지만 UIView -> UIImage로 변환하는 방법에 대해 알아보면서 CGContext 관련 메소드들을 알아보았습니다 ㅎ

오늘 포스팅도 도움이 되었으면 좋겠네요 :)

반응형

'iOS' 카테고리의 다른 글

[iOS] RunLoop란  (2) 2023.03.01
[iOS] Github Action 이용 - Build  (2) 2021.11.06
[iOS] UILabel - 현재 적용된 Line 수 구하기  (0) 2021.09.13
[iOS] Custom Framework 만들기  (0) 2021.06.26
[iOS] WKWebView (2) - 웹 뷰 사용하기  (0) 2021.05.25