관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] UIImage 회전, 이미지 회전 방법 (CGAffineTransform, CGContext, UIGraphicsBeginImageContext, UIGraphicsGetImageFromCurrentImageContext) 본문

iOS 응용 (swift)

[iOS - swift] UIImage 회전, 이미지 회전 방법 (CGAffineTransform, CGContext, UIGraphicsBeginImageContext, UIGraphicsGetImageFromCurrentImageContext)

jake-kim 2021. 8. 21. 00:55

CGAffineTransform

  • 수학적인Affine("아핀") 변환의 의미: 점 사이의 상대적 거리 유지, 평행선을 보존하는 변환
    • translateBy(x:y:): Coordinate를 변경
    • scaleBy(x:y:): 확대 or 축소
    • rotate(by:): 회전

code

    enum AffineType {
        case translate
        case scale
        case rotate
    }
    
    func affineTransfrom(_ type: AffineType) {
        switch type {
        case .translate:
            rectButton.transform = .init(translationX: 50, y: 1.0)
            rectButton.setTitle("(translationX:50, y:1.0)", for: .normal)
        case .rotate:
            rectButton.transform = .init(rotationAngle: 45.0)
            rectButton.setTitle("(rotationAngle:45.0)", for: .normal)
        case .scale:
            rectButton.transform = .init(scaleX: 2.0, y: 1.0)
            rectButton.setTitle("(scaleX:2.0, y:1.0)", for: .normal)
        }
    }

CGContext

  • 2D를 그릴때 사용하는 원석: bitmap image, PDF document, printer와 같이 어떤 것을 그려야할때, 원본에다 각종 affine변환을 사용할 수 있는 객체
  • affine 변환도 해당 context에서 사용가능

이미지 회전 코드

extension UIImage {
    func rotate(degrees: CGFloat) -> UIImage {

        /// context에 그려질 크기를 구하기 위해서 최종 회전되었을때의 전체 크기 획득
        let rotatedViewBox: UIView = UIView(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
        let affineTransform: CGAffineTransform = CGAffineTransform(rotationAngle: degrees * CGFloat.pi / 180)
        rotatedViewBox.transform = affineTransform

        /// 회전된 크기
        let rotatedSize: CGSize = rotatedViewBox.frame.size

        /// 회전한 만큼의 크기가 있을때, 필요없는 여백 부분을 제거하는 작업
        UIGraphicsBeginImageContext(rotatedSize)
        let bitmap: CGContext = UIGraphicsGetCurrentContext()!
        /// 원점을 이미지의 가운데로 평행 이동
        bitmap.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
        /// 회전
        bitmap.rotate(by: (degrees * CGFloat.pi / 180))
        /// 상하 대칭 변환 후 context에 원본 이미지 그림 그리는 작업
        bitmap.scaleBy(x: 1.0, y: -1.0)
        bitmap.draw(cgImage!, in: CGRect(x: -size.width / 2, y: -size.height / 2, width: size.width, height: size.height))

        /// 그려진 context로 부터 이미지 획득
        let newImage: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()

        return newImage
    }
}

rotate 적용

  • 원본

  • imageView.transform =.init(rotationAngle: 45)

  • imageView.image = image?.rotate(degrees: 45): 이미지의 사이즈 그대로 사각형 영역이 생겨서 다른 view와의 위치 조정에 유리

  • .identity 속성: affine변환 후 다시 원래 크기로 되돌리고 싶은 경우 사용

view.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)

/// affine 변환 전의 고유 값으로 되돌리는 코드
containerView.transform = .identity

* 전체 소스코드: https://github.com/JK0369/affineTransformEx

 

* 참고

- https://developer.apple.com/documentation/coregraphics/cgcontext

- https://developer.apple.com/documentation/coregraphics/cgcontext/1456228-rotate

- https://developer.apple.com/documentation/coregraphics/cgcontext/1454659-scaleby

- https://developer.apple.com/documentation/coregraphics/cgcontext/1455286-translateby

- https://stackoverflow.com/questions/51698275/my-portraits-videos-gets-rotated-after-load-from-url-avurlasset

- https://developer.apple.com/documentation/coregraphics/cgaffinetransform

Comments