2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【SwiftUI】 SwiftUIView(init: UIView(init: SwiftUIView))

Posted at

はじめに

タイトルのように、皆んさんこんな画面を作るような時がありますか?
例えば下記のような場面。

  • TextFieldをタップして、キーボードではなく、SwiftUIで書いたカスタムのPickerを出したい

SwiftUIにはUIKitにあるinputViewのような仕組みがないようです。

ここでタイトルのような作りを考えました。

アプローチ

  1. SwiftUIでカスタムのPicker(CustomPicker_SwiftUI)を作成
  2. UITextFieldを継承したクラス(CustomTextField_UIKit)を作成
  3. UIHostingControllerでCustomPickerをラップする(let wrapperView = UIHostingController(rootView: CustomPicker_SwiftUI))
  4. CustomTextFieldのinputViewにwrapperView.viewを設定する
  5. UIViewRepresentableでCustomTextFieldをラップして、SwiftUIで呼び出す

実際のコード

import SwiftUI
import UIKit

// Step1
struct CustomViewPicker: View {
    var tapHandler: (String) -> Void
    
    var body: some View {
        Button {
            tapHandler("Hello World")
        } label: {
            Text("Hello World")
        }
        
    }
}

// Step2
class CustomTextField: UITextField {
    override init(frame: CGRect) {
        super.init(frame: frame)
        initSubView()
    }
    
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        initSubView()
    }
    
    private func initSubView() {
        self.placeholder = "Tap here!"
        // Step3
        let wrapperView = UIHostingController(rootView: CustomViewPicker(tapHandler: setText)).view
        if let wrapperView {
            wrapperView.translatesAutoresizingMaskIntoConstraints = false
            // Step4
            self.inputView = wrapperView
            self.inputAccessoryView = createToolbar()
        }
    }
    
    private func createToolbar() -> UIToolbar {
        let toolbar = UIToolbar()
        toolbar.frame = CGRect(x: 0, y: 0, width: self.frame.width, height: 44)
        
        let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil)
        let doneButton = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(didTapDone))
        
        let toolbarItems = [flexibleSpace, doneButton]
        toolbar.items = toolbarItems
        return toolbar
    }
    
    @objc private func didTapDone(_ sender: UIButton) {
        resignFirstResponder()
    }
    
    private func setText(text: String) {
        self.text = text
    }
}

struct CustomTextFieldRepresentable: UIViewRepresentable {
    func makeUIView(context: Context) -> some UIView {
        return CustomTextField()
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {}
}


struct ContentView: View {
    var body: some View {
        // Step5
        CustomTextFieldRepresentable()
            .frame(height: 44)
            .background(.red)
    }
}

動き

Simulator Screen Recording - iPhone 14 Pro - 2023-09-09 at 13.37.12.gif

終わり

今SwiftUIではinputViewのような仕組みがまだないですが、今後Appleの対応に期待しています。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?