안녕하세요. 후르륵짭짭입니다.
이전 부터 생각한 프로젝트가 있었는데, 마음만 먹고 사실 제대로 작업한 적이 없었습니다.
하지만 이제는 시간도 생겼고 약속도 했기 때문에 만들어보려고 합니다.
그래서 핵심 기능 중에 하나인 이미지 편집 라이브러리인 TOCropViewController와
SwiftUI에서 이것을 사용하는 방법에 대해 작성해보려고 합니다.
** UIViewControllerRepresentable **
SwiftUI에서 UIKit ViewController를 적용할 때 필요한 Adapter 같은 역할을 하는 친구 입니다.
UIViewControllerRepresentable에는 아래 두개의 Protocol을 준수 해줘야합니다.
첫번째로 makeUIViewController는 UIKit view에 대한 초기 설정을 하는 곳 입니다.
예를들어 아래와 같이 사용할 수 있습니다.
func makeUIViewController(context: Context) -> CropViewController {
let viewController = CropViewController(croppingStyle: .default, image: selectedImage)
viewController.aspectRatioPreset = .presetSquare //편집 사이즈
viewController.rotateButtonsHidden = true // 회전 작동 여부
viewController.toolbarPosition = .top
viewController.doneButtonTitle = "Done"
viewController.cancelButtonTitle = "Back"
viewController.doneButtonColor = .blue
viewController.cancelButtonColor = .red
viewController.delegate = context.coordinator
return viewController
}
그리고 context는 UIViewControllerRepresentableContext라는 Struct를 가지고 있습니다.
그리고 여기에 coordinator라는 것이 존재하는데, 이는 View에 특정 기능을 넣어주기 위한 객체를 의미한다.
func makeCoordinator() -> ImageCropCoordinator {
ImageCropCoordinator(parent: self)
}
class ImageCropCoordinator : NSObject , CropViewControllerDelegate{
var parent : CropViewControllerRepresentable
init(parent: CropViewControllerRepresentable) {
self.parent = parent
}
func cropViewController(_ cropViewController: CropViewController, didFinishCancelled cancelled: Bool) {
self.parent.dismiss()
}
func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.parent.croppedImage = image
self.parent.dismiss()
}
func cropViewController(_ cropViewController: CropViewController, didCropToCircularImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.parent.croppedImage = image
self.parent.dismiss()
}
}
위와 같이 makeCoordinator에 반환 값을 임의로 만든 객체를 넣어준다.
그리고 해당 객체에 Delegate를 추가할 수도 있고 여러가지 기능을 할 수 있게 된다.
func makeUIViewController(context: Context) -> CropViewController {
let viewController = CropViewController(croppingStyle: .default, image: selectedImage)
,,, 생략 ,,,
viewController.delegate = context.coordinator
return viewController
}
이렇게 Delegate를 넣어준다. 그러면 ImageCropCoordinator에서 해당 역할을 수행한다.
그리고 마지막으로 updateUIViewController 라는 것이 존재하는데, 이것은 View의 상태값이 변경 될 때 마다 호출이 된다.
struct CropViewControllerRepresentable : UIViewControllerRepresentable {
let selectedImage : UIImage
@Binding var croppedImage : UIImage?
@Environment(\.dismiss) private var dismiss
,,, 생략 ,,,
//Binding으로 된 State가 변경 될 때 호출 된다.
func updateUIViewController(_ uiViewController: CropViewController, context: Context) {
}
class ImageCropCoordinator : NSObject , CropViewControllerDelegate{
var parent : CropViewControllerRepresentable
init(parent: CropViewControllerRepresentable) {
self.parent = parent
}
,,, 생략 ,,,
func cropViewController(_ cropViewController: CropViewController, didCropToCircularImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.parent.croppedImage = image
self.parent.dismiss()
}
}
}
따라서 위의 예시를들면 croppedImage가 변경될 때 updateUIViewController가 호출이 된다.
** TOCropViewController **
https://github.com/TimOliver/TOCropViewController
정말 단순한 이미지 편집 라이브러리 입니다.
정말 간단하게 기본적인 이미지 편집기능을 제공하는데, 나름 쫌 쓸만해 보였다.
let viewController = CropViewController(croppingStyle: .default, image: selectedImage)
위 방법으로 ViewController를 생성해준다. 그리고 각종 필요한 것을 설정해주면 된다.
croppingStyle은 기본 사각형과 원 두개 설정이 가능하다.
viewController.aspectRatioPreset = .presetSquare //편집 사이즈
viewController.rotateButtonsHidden = true // 회전 작동 버튼 숨김 여부
viewController.toolbarPosition = .top // 설정 버튼 위치
viewController.doneButtonTitle = "Done" // 완료 버튼 title
viewController.cancelButtonTitle = "Back" // 취소 버튼 title
viewController.doneButtonColor = .blue // 완료 버튼 색
viewController.cancelButtonColor = .red // 취소 버튼 색
viewController.allowedAspectRatios = [.preset16x9, .preset3x2, .preset4x3] //편집 사이즈
viewController.showActivitySheetOnDone = true // 공유하기 여부
제대로 된 값을 확인하기 위해서는 CropViewControllerDelegate 를 사용해야한다.
// 취소 버튼을 눌렀을 경우
func cropViewController(_ cropViewController: CropViewController, didFinishCancelled cancelled: Bool) {
self.parent.dismiss()
}
// Default(사각형)의 완료 버튼을 눌렀을 경우
func cropViewController(_ cropViewController: CropViewController, didCropToImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.parent.croppedImage = image
self.parent.dismiss()
}
// circle(원)의 완료 버튼을 눌렀을 경우
func cropViewController(_ cropViewController: CropViewController, didCropToCircularImage image: UIImage, withRect cropRect: CGRect, angle: Int) {
self.parent.croppedImage = image
self.parent.dismiss()
}
** 참고 사이트 **
https://ios-development.tistory.com/1043
https://velog.io/@j_aion/SwiftUI-UIViewControllerRepresentable
https://www.youtube.com/watch?v=XPcuAg0Xctw
SwiftUI Gesture :
https://daesiker.tistory.com/55
Alert :
https://www.hohyeonmoon.com/blog/swiftui-tutorial-alert/
AppDelegate를 SwiftUI에 적용하는 방법 :
댓글