SwiftUI

SwiftUI 사진 가져오기(2)

Daesiker 2021. 3. 19. 11:47
반응형

이전 게시물 보기

MemeTextField.swift

위쪽과 아래쪽에 있는 Text컴포넌트의 UI를 꾸며주는 코드이다.

import SwiftUI

struct MemeTextField: View {
  @Binding var text: String

  var body: some View {
    TextField(text, text: $text)
      .multilineTextAlignment(.center)
      .lineLimit(nil)
      .foregroundColor(.black)
      .font(Font.system(size: 25, weight: .bold))
      .textCase(.uppercase)
  }
}

@Binding 어노테이션을 사용하여 text 변수를 사용하였는데 이렇게 지정한 이유는 여러개의 뷰에서 동시에 State 값을 참조가 가능하기 때문이다. 하나의 View에서만 참조를 할 때에는 State 어노테이션을 사용하고 여러개의 View에서 참조할 때에는 Binding 어노테이션을 사용한다.

 

ImageLayer.swift

import SwiftUI

struct ImageLayer: View {
  @Binding var imageData: Data?

  var body: some View {
    NSUIImage.image(fromData: imageData ?? Data())
      .resizable()
      .aspectRatio(contentMode: .fit)
  }
}

NSUIImage는 macOS에서는 NSImage로 데이터를 가져오고 iOS에서는 UIImage로 데이터를 가져올 수 있게 도와주는 컴포넌트이다. 이것은 iOS 폴더와 macOS 폴더에서 직접 만든것으로 여기서는 그냥 Image 컴포넌트랑 같다고 생각하면 된다. 이미지를 선택하지 않을 수도 있으므로 imageData를 옵셔널로 선언을 했다.

 

TextLayer.swift

import SwiftUI

struct TextLayer<ImageContent: View>: View {
  @Binding var meme: Meme
  let imageContent: () -> ImageContent

  var body: some View {
    ZStack(alignment: .bottom) {
      ZStack(alignment: .top) {
        imageContent()
        MemeTextField(text: $meme.topText)
      }

      MemeTextField(text: $meme.bottomText)
    }
  }
}

ImageContent라는 View Generic을 가지고 있는 TextLayer 컴포넌트이다. 여기는 이미지 데이터가 들어가는 View 컴포넌트이다. ZStack안에 앞서 만든 MemeTextField 2개와 ImageContent를 넣음으로써 뼈대가 완성되었다.

 


IOS

macOS와 iOS에서 같은 이름의 파일이지만 소스코드가 다를경우 _OS 명을 이름뒤에 붙이면 알아서 구분을 해준다.

NSUIImage_iOS.swift

import UIKit
import SwiftUI

public typealias NSUIImage = UIImage

extension NSUIImage {
  var data: Data? {
    return self.pngData()
  }

  static func image(fromData data: Data) -> Image {
    return Image(uiImage: UIImage(data: data) ?? UIImage())
  }
}

우선 NSUIImage를 typealias 키워드를 통해 UIImage와 같도록 바꾸어준다. 그리고 extension을 통해 data를 pngData()로 바꾸어주고 image 함수를 통해 SwiftUI의 Image 컴포넌트안에 들어갈 데이터를 UIImage(data: data)로 바꾸어서 변환해준다.

 

UIImagePicker.swift

import SwiftUI

struct UIImagePicker: UIViewControllerRepresentable {
  class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
    let parent: UIImagePicker

    init(_ parent: UIImagePicker) {
      self.parent = parent
    }

    func imagePickerController(
      _ picker: UIImagePickerController,
      didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]
    ) {
      if let uiImage = info[.originalImage] as? UIImage {
        parent.image = uiImage
      }

      parent.presentationMode.wrappedValue.dismiss()
    }
  }

  @Environment(\.presentationMode) var presentationMode
  @Binding var image: UIImage?

  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }

  func makeUIViewController(
    context: UIViewControllerRepresentableContext<UIImagePicker>
  ) -> UIImagePickerController {
    let picker = UIImagePickerController()
    picker.delegate = context.coordinator
    return picker
  }

  func updateUIViewController(
    _ uiViewController: UIImagePickerController,
    context: UIViewControllerRepresentableContext<UIImagePicker>
  ) {
  }
}

여기 코드는 사진파일을 받기 위한 UIImagePicker을 설정하는 부분이다. 이미지를 선택할 때 사진 App에 들어가서 이미지를 가져올 때 사용한다. UIImagePicker 부분은 나중에 UIKit 프레임워크 카테고리를 만들어서 자세하게 다루어볼 예정이다. 여기서는 Info.plist 파일에 사진 파일에 접근할 수 있는 권한을 설정해야된다는 것만 말하고 넘어갈 것이다. 그리고 새로운 어노테이션이 보이는데 바로 @Environment이다.

이전에 있었던 State와 Binding은 View에서 사용하는 변수를 공유하는 것이라면 Environment는 View의 환경설정을 공유하는 것이다. 여기서는 SwiftUI의 뷰와 UIImagePicker의 presentationMode를 공유하고 있다.

 

MemeEditor_iOS.swift

import SwiftUI

struct MemeEditor: View {
  @Binding var meme: Meme
  @State var showingImagePicker = false
  @State private var inputImage: NSUIImage?

  func loadImage() {
    guard let inputImage = inputImage else { return }
    meme.imageData = inputImage.data
  }

  var body: some View {
    TextLayer(meme: $meme) {
      Button {
        showingImagePicker = true
      } label: {
        if meme.imageData != nil {
          ImageLayer(imageData: $meme.imageData)
        } else {
          Text("Add Image")
            .foregroundColor(.white)
            .padding()
            .background(Color("rw-green"))
            .cornerRadius(30)
            .padding(.vertical, 50)
        }
      }
    }
    //isPresented 값이 true일 때 실행
    .sheet(isPresented: $showingImagePicker, onDismiss: loadImage) {
      UIImagePicker(image: self.$inputImage)
    }
  }
}

여기서는 showingImagePicker 변수를 통해 UIImagePicker를 불러와서 사진을 업로드할 수 있게 해놓았다. 버튼을 클릭하면 showingImagePicker가 true로 바뀌면서 UIImagePicker를 부른다. 사진을 선택하면 아까 @Environment으로 바인딩한 presentationMode가 dismiss가 되면서 UIImagePicker가 종료되고 .sheet의 onDismiss 파라미터에 있는 loadImage 함수가 실행된다.

반응형

'SwiftUI' 카테고리의 다른 글

SwiftUI Profile View  (0) 2021.03.30
SwiftUI 사진 가져오기(3)  (0) 2021.03.20
SwiftUI 사진 가져오기(1)  (2) 2021.03.17
SwiftUI TabView  (1) 2021.03.02
SwiftUI GroupBox, Menu  (0) 2021.02.26