[TIL]04.17


UIKETと一緒に使うSWIFTUI


SWIFTUIからUIViewを追加します。


UIViewRepresentable Protoclを使用する必要があります
UIViewRepresentableに準拠した構造を作成し、直ちにSWIFTUIで使用します.
この構造は、2つの方法を実装する必要があります.
  • func makeUIView(コンテキスト:Context)->UIKEtのタイプ
  • SWIFTUIに表示するビューを返します.
  • func updateUIView(uiView:UImitのタイプ、コンテキスト:Context)
  • SWIFTUIは更新発生時に動作します.
  • final class RedLabel: UILabel {
        override func awakeFromNib() {
            super.awakeFromNib()
    
            textColor = .red
        }
    }
    
    struct RepresentableRedLabel: UIViewRepresentable  {
        var text: String
    
        let redLabel = RedLabel()
    
        func makeUIView(context: Context) -> UILabel {
            redLabel
        }
        func updateUIView(_ uiView: UILabel, context: Context) {
            redLabel.text = text
        }
    }
    
    struct ContentView: View {
        var body: some View {
            RepresentableRedLabel(text: "내용")
        }
    }
    // UILabel의 RedLabel을 SwiftUI에서 사용하는 경우입니다.

    SWIFTUIからUI ViewControlを追加


    SWIFTUIでUI ViewControllerを使用するには、UI ViewController Representableが必要です.
    UIViewControllerRepresentableを実装するには、2つの方法が必要です.
    – func makeUIViewController(context: Context) -> some UIViewController
    – func updateUIViewController(_ uiViewController: some UIViewController, context: Context)
    struct ImagePickerController: UIViewControllerRepresentable {
        func makeUIViewController(context: Context) -> UIImagePickerController{
            UIImagePickerController()
        }
        func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
    }
    
    struct ContentView: View {
        var body: some View {
            ImagePickerController()
        }
    }
    Coordinatorを使用してSWIFTUIにUI画像を表示するコード
    struct ImagePickerController: UIViewControllerRepresentable {
        // SwiftUI 에서의 부모뷰의 @State property부터의 Binding
        @Binding var selectedImage: Image?
        @Binding var existSelectedImage: Bool
     
        func makeUIViewController(context: Context) -> UIImagePickerController {
            let imagePickerController = UIImagePickerController()
            imagePickerController.delegate = context.coordinator
     
            return imagePickerController
        }
     
        func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
     
        //  Cordinator 대입.
        func makeCoordinator() -> Coordinator {
            Coordinator(selectedImage: $selectedImage, existSelectedImage: $existSelectedImage)
        }
     
    }
     
    extension ImagePickerController {
        // Cordinator 만들기
        final class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
            @Binding var selectedImage: Image?
            @Binding var existSelectedImage: Bool
    
            // 밑줄을 사용해서 초기화 해야한다. 프로퍼티 래퍼임을 알려주기 위해서.
            init(selectedImage: Binding<Image?>, existSelectedImage: Binding<Bool>) {
                _selectedImage = selectedImage
                _existSelectedImage = existSelectedImage
            }
     
            func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
                guard let selectedOriginalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage else { return }
     
                selectedImage = Image(uiImage: selectedOriginalImage)
                existSelectedImage = true
            }
        }
     
    }
     
    struct ContentView: View {
        @State private var selectedImage: Image?
        @State private var existSelectedImage = false
     
        var body: some View {
            ZStack {
                VStack {
                    selectedImage?
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .frame(width: 100.0, height: 100.0)
                    Button(action: didTapSelectedImageButton) {
                        Text("Select Image")
                    }
                }
     
                if selectedImage == nil {
                    ImagePickerController(
                        selectedImage: $selectedImage,
                        existSelectedImage: $existSelectedImage
                    )
                }
            }
        }
     
        private func didTapSelectedImageButton() {
            selectedImage = nil
            existSelectedImage = false
        }
    }
    
    // @State property와 Binding으로 Coordinator에서 부모 뷰에 업데이트를 별도로 하지 않아도 간단하게 구현함
    既存のUIViewまたはUIViewControllerでSWIFTUI Viewを使用する場合、UIViewRepresentableに準拠するStructを個別に作成する必要がない場合は、UImit classでExtensionインプリメンテーションを使用できます.
    final class Label: UILabel {}
     
    //  Extension 으로 정의
    extension Label: UIViewRepresentable {
        func makeUIView(context: Context) -> UILabel {
            backgroundColor = .gray
            text = "This is UIKit UILabel"
     
            return self
        }
     
        func updateUIView(_ uiView: UILabel, context: Context) {}
    }
     
    struct ContentView: View {
        var body: some View {
            VStack {
                Text("This is Swift UI Text")
                Label()
            }
        }
    }

    UIKETからSWIFTUIを追加します。


    UIHosting Controlを使用する必要があります.
    メソッドやプロパティを追加する必要はありません.
    UI HostingController(RootView:UIキットに表示されるSwitUI):SwitUIビューを初期化して挿入すると終了します
    struct ContentView: View {
        var body: some View {
            Text("내용")
                .fontWeight(.bold)
        }
    }
     
    class ViewController: UIViewController {
        override func viewDidLoad() {
            super.viewDidLoad()
     
            
            let hostingController = UIHostingController(rootView: ContentView())
            hostingController.view.translatesAutoresizingMaskIntoConstraints = false
            hostingController.view.frame = view.bounds
     
            addChild(hostingController)
            view.addSubview(hostingController.view)
        }
    }
    UI HostingControllerのrootViewにSwitUIを入れるだけです.addChild、addSubViewは、これまでUIツールパッケージにUI ViewControlを追加したときと同じです.
    struct ContentView: View {
        var body: some View {
            Text("펭 - 하!")
                .fontWeight(.bold)
        }
    }
     
    class ViewController: UIViewController {
     
        @IBAction private func didTapPresentButton(_ sender: Any) {
            let hostingController = UIHostingController(rootView: ContentView())
            present(hostingController, animated: true)
        }
     
    }
    SWIFTUI画面を表示する場合、SWIFTUIビューをUI Hosting Controllerでラップする以外、他のUIキットコードは常に使用できます.

    SWIFTUIビューを変数として他のビュー構造に渡します。


    @ViewBuilderの使用

    struct ContainerView<Content: View>: View {
        @ViewBuilder var content: Content
    
        var body: some View {
            content
        }
    }
    // 뷰에 단순히 넣는것 뿐만 아니라, if-else, switch-case 블럭에서도 사용 가능하다.
    struct SimpleView: View {
        var body: some View {
            ContainerView{
                Text("SimpleView Text")
            }
        }
    }
    
    struct IfElseView: View{
        var flag = true
        
        var body: some View {
            ContainerView{
                if flag {
                    Text("True text")
                } else {
                    Text("False text")
                }
            }
        }
    }
    
    struct SwitchCaseView: View {
        var condition = 1
    
        var body: some View {
            ContainerView {
                switch condition {
                    case 1:
                        Text("one")
                    default:
                        Text("Default")
                }
            }
        }
    }

    ジェニーリックと頭文字でログインする方法

    struct ParentView: View {
        var body: some View {
            NavigationView{
    
                VStack(spacing: 8){
    
                    ChildView(destinationView: Text("View1"), title: "1st")
                    ChildView(destinationView: Text("View2"), title: "2nd")
                    Spacer()
                }
                .padding(.all)
                .navigationBarTitle("NavigationLinks")
            }
        }
    }
    
    struct ChildView<Content: View>: View {
        var destinationView: Content
        var title: String
    
        init(destinationView: Content,  title: String) {
            self.destinationView = destinationView
            self.title = title
        }
    
        var body: some View {
            NavigationLink(destination: destinationView){
                Text("This item opens the \(title) view").foregroundColor(Color.black)
            }
        }
    }

    SwiftUI | List (UITableView)


    SWIFTUIではUITableViewがListです.

    List


    UIKETのUITAbleViewDelegateやUITAbleViewDataSourceを使わずに簡単に実現できます.
    struct ContentView: View {
        var body: some View {
            List {
                Text("1")
                Text("2")
                Text("3")
            }
        }
    }
    追加アイテムのカスタマイズも簡単です
    struct CustomCell: View {
        var body: some View {
            HStack {
                Image(systemName: "tortoise.fill")
                Text("거북이")
            }
        }
    }
     
    struct ContentView: View {
        var body: some View {
            List {
                CustomCell() 
                Text("1")
                Text("2")
                Text("3")
            }
        }
    }
    リストがナビゲーションビューのサブビューとして宣言され、Cellがナビゲーションリンクによって囲まれている場合、Cellをタッチすることによって他のビューに移動することができる.

    アレイ内のデータをListとして表示する


    リストに表示されるArrayの内容は、Identificalプロトコルに従うclass、structとして定義する必要があります.
    struct Item: Identifiable {
        var id = UUID() // Int, String 등 hashable을 따르는 것들은 뭐든지 가능
        let name: String
    } 
    必ずリスト内で認識できるid propertyが必要!
    struct ContentView: View {
        let items: [Item] = [Item(name: "asdf"), Item(name: "sadfasf")]
    
        var body: some View {
            NavigationView {
                List(items) {item in
                    Text(item.name)
                }
                .listStyle(GroupedListStyle())
                .navigationBarTitle("List View")
            }
        }
    }

    削除リスト項目の変更


    まずitemsはステータスpropertyとして存在しなければならない.したがって、上記のコードのitemsを@State private var itemsに変換し、次のコードのように関数を追加することができる.
    List {
        // ForEach로 각각의 cell에 delete 메소드를 입력시켜주어야한다.
        ForEach(items) { item in
                Text(item.name)
            }.onDelete(perform: didDeleteCell)
        }
        .listStyle(GroupedListStyle())
        .navigationBarTitle("List View")
    
    // parameter 에는 반드시 offsets 을 추가해야 함.
    func didDeleteCell(at offsets: IndexSet) {
        items.remove(atOffsets: offsets) 
        print("Deleted cell is \(offsets)")
    }
    コードの変更(セルの順序を変更可能)
    List {
        ForEach(items) { item in
            Text(item.name)
        }.onMove(perform: didMoveCell)
    }
    .listStyle(GroupedListStyle())
    .navigationBarTitle("List View")
    .navigationBarItems(trailing: EditButton()) // 수정 버튼이 들어갈 Item
    
    func didMoveCell(form source: IndexSet, to destination: Int) {}
    リファレンス
    https://unnnyong.com/2020/05/23/swiftui-uikit-%ea%b3%bc-%ed%95%a8%ea%bb%98-%ec%82%ac%ec%9a%a9%ed%95%98%ea%b8%b0-uiviewrepresentable-uiviewcontrollerrepresentable-uihostingcontroller/#comment-25
    https://stackoverflow.com/questions/56938805/how-to-pass-one-swiftui-view-as-a-variable-to-another-view-struct?rq=1
    https://unnnyong.com/2020/05/22/swiftui-list-%ea%b5%ac-uitableview/