0
1

More than 3 years have passed since last update.

【SwiftUI】プロパティラッパーに関する考察

Posted at

1. 目的

 SwiftUIでは、View(画面のstruct)が状態を保持しないので、プロパティラッパーを使用して状態を管理します。この記事では、複数行のテキスト(UIKitでのUITextView)を例に考察します。

2. コードの例

 目的に関連するコードだけを抜粋しています。やりたいことは、プレースホルダー付きのUITextViewに相当するViewをSwiftUIで実装することです。
 プレースホルダーは文字の入力があったら非表示とするため、#1のisEditedフラグを使用します。

 この記事のポイントは、@Stateプロパティのtextです。
 MultiLineTextField_Rep(子)の#2-2から、MultiLineTextField(親)の#2-1に入力値を渡しています。この受け渡しがない場合、#1のisEditedフラグが変更になってMultiLineTextField(親)が再描画される際、入力した値は引き継がれません(=Viewが状態を保持しないから)。
 このときのUIの動作としては、キーボードの文字をタップすると、プレースホルダーが非表示になって、文字も入力されていない(1文字目が無視される)ように見えます。実際は、Viewが入力値(textの値)を保持せず消失しています。

import SwiftUI

struct MultiLineTextField: View {
    @State var isEdited: Bool = false      //#1-1
    @State var text: String = ""     //#2-1
    var body: some View {
        ZStack(){
            if !isEdited {
                Placeholder()
            }
            MultiLineTextField_Rep(text: $text, isEdited: $isEdited)
        }
    }
}

// Placeholder用のUILabel
struct Placeholder: UIViewRepresentable {
省略
}

struct MultiLineTextField_Rep: UIViewRepresentable {

    @Binding var text: String
    @Binding var isEdited: Bool
    let textView = UITextView()

    final class Coordinator: NSObject, UITextViewDelegate {

        private var parent: MultiLineTextField_Rep
        init(_ textView: MultiLineTextField_Rep) {
            self.parent = textView
            super.init()
            self.setNotification()
        }

     (省略

        private func setNotification() {
            // テキストが編集されたらPlaceholderを非表示とするための通知
            let notificationCenter = NotificationCenter.default
            notificationCenter.addObserver(self,
                                           selector: #selector(textChanged(_:)),
                                           name: UITextView.textDidChangeNotification,
                                           object: nil)
        }

        @objc func textChanged(_ notification: NSNotification) {
            parent.isEdited = parent.text.isEmpty ? false : true     //#1-2
        }

        func textViewDidChange(_ textView: UITextView) {
            self.parent.text = textView.text       //#2-2
            self.parent.textView.attributedText = NSAttributedString(string: textView.text,
                                                                     attributes: self.parent.textAttribute)
        }

    }
}
0
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
0
1