UIImage
resizing
UIImage
가 너무 큰 경우 보통은 auto layout을 통해 너비,높이를 잡아주거나 top,leading,trailing,bottom을 주어서 정해진 마진을 지키면서 이미지가 들어가도록 구현한 경험이 있을 것이다.
만약 이미지 자체의 크기를 조절하고 싶다면 위 방법으로는 해결이 되지 않을 것이다. 이에 사용할 수 있는 방식이 이미지 리사이징이다.
이미지 리사이징을 위해 사용되는 방법은 여러가지가 있지만 이번에는 UIGraphicsBeginImageContextWithOptions(_:_:_:)
를 사용하는 방식을 알아보자.
간략하게 순서를 먼저 알아보자.
UIGraphicsBeginImageContextWithOptions(_:_:_:)
로 비트맵 만들기UIView.draw()
로 전달받은 사이즈만큼 다시 그려주기UIGraphicsGetImageFromCurrentImageContext()
메서드를 통해 크기가 조정된 이미지를 받아오기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