👨🏻‍💻iOS 공부/iOS & Swift

[iOS] UIImage resizing (feat. UIGraphics)

728x90
반응형

UIImage resizing

UIImage가 너무 큰 경우 보통은 auto layout을 통해 너비,높이를 잡아주거나 top,leading,trailing,bottom을 주어서 정해진 마진을 지키면서 이미지가 들어가도록 구현한 경험이 있을 것이다.

 

만약 이미지 자체의 크기를 조절하고 싶다면 위 방법으로는 해결이 되지 않을 것이다. 이에 사용할 수 있는 방식이 이미지 리사이징이다.

이미지 리사이징을 위해 사용되는 방법은 여러가지가 있지만 이번에는 UIGraphicsBeginImageContextWithOptions(_:_:_:)를 사용하는 방식을 알아보자.

 

간략하게 순서를 먼저 알아보자.

  1. UIGraphicsBeginImageContextWithOptions(_:_:_:)로 비트맵 만들기
  2. UIView.draw()로 전달받은 사이즈만큼 다시 그려주기
  3. UIGraphicsGetImageFromCurrentImageContext() 메서드를 통해 크기가 조정된 이미지를 받아오기
  4. UIGraphicsEndImageContext()를 호출하여 비트맵을 그려주는 환경을 치워주기!

1. UIGraphicsBeginImageContextWithOptions(_:_:_:)

func UIGraphicsBeginImageContextWithOptions(_ size: CGSize, 
                                          _ opaque: Bool, 
                                          _ scale: CGFloat)

특정 옵션의 비트맵 기반의 그래픽 배경을 만들어주는 역할을 한다. 총 세 가지의 파라미터가 있다.

  • size
    • 새로운 비트맵 배경(context)의 크기를 나타낸다.
    • 이는 UIGraphicsGetImageFromCurrentImageContext() 메서드를 호출하여 반환할 이미지의 크기를 나타내기도 한다.
    • 비트맵의 크기를 픽셀단위로 가져오려면, 이미지의 너비와 높이에 scale 파라미터로 오게 될 값을 곱해주면 된다.
    • 너비 * 높이 * scale을 의미하는 것이다.
  • opaque
    • 비트맵이 불투명한지 여부를 나타내는 Bool 타입의 값이다.
    • 비트맵이 완전히 불투명하다는 것을 알고 있다면 값을true로 하여 alpha 채널을 무시하고 비트맵의 저장소를 최적화 할 수 있다.
    • false로 할 경우, 비트맵에 부분적으로 투명한 픽셀을 처리할 수 있는 alpha 채널이 포함되어야 한다는 것을 의미한다.
  • scale
    • 비트맵에 적용할 축적 비율(scale factor)이다.
    • 값을 0.0으로 주면 scale factor가 기기의 main screen의 scale factor로 설정된다.
    • 기기마다 스케일 값은 다른데, 스케일 값은 UIScreen.main.scale로도 확인해 볼 수 있다.

이 메서드를 통해 생성된 배경(context)가 현재 배경이며, UIGraphicsGetImageFromCurrentImageContext() 메서드를 호출하여 배경의 현재 내용을 기반으로 이미지 객체를 얻을 수 있다. 그리고 배경의 수정을 마치면 무조건 UIGraphicsEndImageContext() 메서드를 호출해서 비트맵을 그리는 환경을 정리하고, 배경 스택(context stack)의 맨 위에서 그래픽 배경(graphic context)를 제거해야한다. 스택에서 이러한 타입의 배경을 제거하는데 UIGraphicsPopContext()메서드를 사용하면 안된다고 한다.

 

만약 다른 그래픽 배경을 가져오고 싶다면 push/pop을 하여 배경을 변경할 수 있다. 또한 UIGraphicsGetCurrentContext() 메서드를 사용하여 비트맵 배경을 가져올 수 있다.

 

마지막으로 이 메서드의 경우 앱의 모든 쓰레드에서 호출할 수 있다고 하니 참고하자.

2. draw(in: )

func draw(in rect: CGRect)

전체 이미지를 특정한 사각형 내에 그려주어 필요에 따라 크기를 조정해주는 역할을 한다.

파라미터는 1개로 간단하다.

  • rect
    • 이미지를 그려줄 사각형(그래픽 context의 좌표계에서의 사각형!)을 의미한다.

이 메서드는 설정된 이미지의 방향을 고려하여 현재 그래픽 배경에 전체 이미지를 그려준다. 기본 좌표계에서 이미지는 지정된 사각형의 원점 오른쪽 아래쪽에 위치한다. 하지만 이 방법은 현재 그래픽 배경에 적용된 모든 변환들을 고려해준다.

 

이 메서드는 CGBlendMode.normal blend 모드를 사용하여 이미지를 불투명하게 그려준다.

 

이제 이미지를 그렸으니 얻어오기만 하면 된다.

3. UIGraphicsGetImageFromCurrentImageContext

func UIGraphicsGetImageFromCurrentImageContext() -> UIImage?

옵셔널 타입의 UIImage를 반환해주는 메서드이다. 이제 크기가 조정된 이미지를 받아줄 수 있는 것이다. 조금 더 자세히 이야기해보면, 현재 비트맵 그래픽 배경의 내용을 포함하는 이미지 객체를 반환해주는 것이다.

 

다만 비트맵 기반 배경이 현재 그래픽 배경인 경우에만 이 메서드를 호출해야 한다. 즉, 앞에서 그래픽 배경(context)를 설정해주고 나서 호출해줘야 한다는 것이다. 그렇지 않고 현재 배경이 nil이거나 아직 UIGraphicsBeginImageContext(_:) 메서드 호출을 통해 생성되지 않은 경우에 위 메서드는 nil을 반환하게 된다.

 

이 메서드 또한 앱의 모든 쓰레드에서 호출될 수 있다!

 

UIGraphicsBeginImageContext(_:)는 또 뭔가..싶긴한데 아까 1번 과정에서 본 메서드랑 굉장히 유사하게 생긴 것을 알 수 있다.

 

실제로도 문서를 보면 UIGraphicsBeginImageContextWithOptions(_:_:_:)의 opaque가 false이고, scale은 1.0으로 세팅된 메서드를 호출하는 것과 같다고 한다. 그래서인지 CGRect만 받도록 아래와 같이 구현되어 있다.

func UIGraphicsBeginImageContext(_ size: CGSize)

4. UIGraphicsEndImageContext()

func UIGraphicsEndImageContext()

스택의 맨 위에서 현재 비트맵 기반 그래픽 배경을 제거하는 역할을 한다. 즉, 다 그리고 이미지를 사용하고나면 제거해주는 것이다.

 

이 메서드를 사용하여 UIGraphicsBeginImageContext(_:) 메서드로 생성된 환경을 정리하고, 스택 상단에서 해당 비트맵 기반 그래픽 배경을 제거할 수 있다. 현재 배경이 UIGraphicsBeginImageContext(_:) 메서드를 통해 생성되지 않은 경우 이 메서드는 아무것도 수행하지 않는다!

 

이 메서드 또한 앱의 모든 쓰레드에서 호출될 수 있다.

5. 구현 코드

설명이 조금 길었지만... 위 내용을 이해했다면 코드는 안봐도 구현할 수 있을 것이다.

UIView를 extension하여 위 메서드들을 사용해서 이미지를 리사이징하는 방법을 구현해보자!

extension UIImage {
    func resizeImageTo(size: CGSize) -> UIImage? {
        // 비트맵 생성
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        // 비트맵 그래픽 배경에 이미지 다시 그리기
        self.draw(in: CGRect(origin: CGPoint.zero, size: size))
        // 현재 비트맵 그래픽 배경에서 이미지 가져오기
        guard let resizedImage = UIGraphicsGetImageFromCurrentImageContext() else {
            return nil
        }
        // 비트맵 환경 제거 
        UIGraphicsEndImageContext()
        // 크기가 조정된 이미지 반환
        return resizedImage
    }
}

그냥 앞선 설명을 그대로 코드로 갖다 넣은 정도이다!


이제 입맛에 맞게 이미지 크기를 조정해서 사용해 볼 수 있을 것이다.

이미지 크기 자체가 줄어든다는 장점도 있겠지만, 이미지 자체가 줄어들기에 이미지 용량 또한 줄어든다는 것도 장점으로 꼽을 수 있을 것 같다.

 

끄읕

 


Ref

 

https://developer.apple.com/documentation/uikit/1623922-uigraphicsbeginimagecontext

https://developer.apple.com/documentation/uikit/1623912-uigraphicsbeginimagecontextwitho

https://developer.apple.com/documentation/uikit/1623924-uigraphicsgetimagefromcurrentima

https://developer.apple.com/documentation/uikit/1623933-uigraphicsendimagecontext

728x90
반응형