[Swift/SwiftUI]subscript를 사용하여 편하게 바인딩

32607 단어 SwiftSwiftUItech

정황


너는 다음과 같은 것이 있다enum.Binding<検索条件>를 수락한 후 이 검색 조건SearchView을 설정합니다.
enum 検索条件 {
    case 完全一致(String)
    case 前方一致(String)
    case 部分一致(String)

    enum タイプ {
        case 完全一致, 前方一致, 部分一致

        var ジェネレータ: (String) -> 検索条件 {
            switch self{
            case .完全一致: return 検索条件.完全一致
            case .前方一致: return 検索条件.前方一致
            case .部分一致: return 検索条件.部分一致
            }
        }
    }
}
간단하게 실시해 봅시다.
struct SearchView1: View {
    @Binding var 条件: 検索条件
    @State var タイプ: 検索条件.タイプ = .部分一致
    @State var 中身 = ""
    var body: some View {
        VStack{
            Picker("タイプを設定", selection: $タイプ){
                Text("完全一致").tag(検索条件.タイプ.完全一致)
                Text("前方一致").tag(検索条件.タイプ.前方一致)
                Text("部分一致").tag(検索条件.タイプ.部分一致)
            }
            TextField("中身", text: $中身)
        }
        .onChange(of: タイプ){ value in
            self.update()
        }
        .onChange(of: 中身){ value in
            self.update()
        }
    }

    func update(){
        条件 = タイプ.ジェネレータ(中身)
    }
}
언뜻 보면 좋은데 자세히 보면 신경 쓰이는 부분이 있어요.
  • 두 개onChange를 먼저 정의했지만 미묘했다.처리는 같지만 두 가지 불쾌함을 써야 한다.
  • View의 외측 변경条件시에는 검출할 수 없습니다.따라서 외부에 기반한 변경クエリタイプ은 변경할 수 없으며 상황에 따라 상태가 편차가 발생할 수 있다.고장이 뚜렷하다.
  • subscript로 해결


    그곳subscript에서 활약하고 있다.우선 다음extension에서 두 개subscript를 정의한다.
    extension 検索条件 {
        enum タイプキー {case タイプ}
        subscript(_ key: タイプキー) -> タイプ {
            get {
                switch self {
                case .完全一致: return .完全一致
                case .前方一致: return .前方一致
                case .部分一致: return .部分一致
                }
            }
            set {
                let 中身 = self[.中身]
                self = newValue.ジェネレータ(中身)
            }
        }
    
        enum 中身キー {case 中身}
        subscript(_ key: 中身キー) -> String {
            get {
                switch self {
                case let .完全一致(value), let .前方一致(value), let .部分一致(value):
                    return value
                }
            }
            set {
                let タイプ = self[.タイプ]
                self = タイプ.ジェネレータ(newValue)
            }
        }
    }
    
    이 기능을 가져오면 다음과 같이 쓸 수 있습니다SearchView.
    struct SearchView2: View {
        @Binding var 条件: 検索条件
        var body: some View {
            VStack{
                Picker("タイプを設定", selection: $条件[.タイプ]){
                    Text("完全一致").tag(検索条件.タイプ.完全一致)
                    Text("前方一致").tag(検索条件.タイプ.前方一致)
                    Text("部分一致").tag(検索条件.タイプ.部分一致)
                }
                TextField("中身", text: $条件[.中身])
            }
        }
    }
    
    는 내부 여분의 변수가 아니라 직접 귀속条件에서 얻은 값이기 때문에 외부条件에서 변경이 발생하더라도 상태에 편차가 생기지 않는다.또 onChange도 사라지고 본질적인 부분만 남았다.
    시용용View도 준비했다.
    struct SearchViewTest: View {
        @State private var 条件1: 検索条件 = .部分一致("")
        @State private var 条件2: 検索条件 = .部分一致("")
    
        var body: some View {
            SearchView1(条件: $条件1)
            Text(verbatim: "\(条件1)").font(.caption)
            Button("リセット"){
    	    //SearchViewの外部から条件を変更してみる
                条件1 = .部分一致("")
            }
            .font(.caption)
            SearchView2(条件: $条件2)
            Text(verbatim: "\(条件2)").font(.caption)
            Button("リセット"){
    	    //SearchViewの外部から条件を変更してみる
                条件2 = .部分一致("")
            }
            .font(.caption)
        }
    }
    

    작업 정보

    extension 부분의 보충 설명.
    우선 extension에 추가된 subscript 정의에서 내부 정의enum를 매개 변수로 한다.이렇게 하면 条件[.タイプ] 또는条件[.中身]이라는 표기를 실현할 수 있다.
    enum タイプキー {case タイプ}
    subscript(_ key: タイプキー) -> タイプ
    
    enum 中身キー {case 中身}
    subscript(_ key: 中身キー) -> String
    
    또한Binding형은 사용KeyPathdynamicMemberLookup와 대응한다.이렇게 하면 $条件[dynamicMember: \.[.タイプ]]$条件[.タイプ]로 약기할 수 있다.
    또한 귀속subscript하면 수치는 그getset를 통해 달라진다.이것은 $array[0]와 같은 귀속을 사용한 동작이다.

    별법


    별도로 사용하지 않더라도subscript 다음과 같은 정의를 할 수 있다.subscript 내의 처리는 각각 Bindinggetset 매개 변수로 지정되어 거의 변화가 없다.동작도 SearchView2와 똑같다.
    struct SearchView3: View {
        @Binding var 条件: 検索条件
    
        var タイプ: Binding<検索条件.タイプ> {
            .init(
                get: {
                    switch 条件 {
                    case .完全一致: return .完全一致
                    case .前方一致: return .前方一致
                    case .部分一致: return .部分一致
                    }
                },
                set: { newValue in
                    let 中身 = self.中身.wrappedValue
                    条件 = newValue.ジェネレータ(中身)
                }
            )
        }
    
        var 中身: Binding<String> {
            .init(
                get: {
                    switch 条件 {
                    case let .完全一致(value), let .前方一致(value), let .部分一致(value):
                        return value
                    }
                },
                set: { newValue in
                    let タイプ = self.タイプ.wrappedValue
                    条件 = タイプ.ジェネレータ(newValue)
                }
            )
        }
    
        var body: some View {
            VStack{
                Picker("タイプを設定", selection: タイプ){
                    Text("完全一致").tag(検索条件.タイプ.完全一致)
                    Text("前方一致").tag(検索条件.タイプ.前方一致)
                    Text("部分一致").tag(検索条件.タイプ.部分一致)
                }
                TextField("中身", text: 中身)
            }
        }
    }
    
    이 방법도 좋은 것 같지만 몇 가지 측면에서 subscript하는 방법을 추천합니다.

  • 사용subscript의 경우 스위프트 UI에서 상징적으로 사용된$ 기호를 유지할 수 있어 가독성이 높다.
  • subscript를 사용하는 경우 액세스 값 자체에 대해 쓰지 않아도 됩니다.wrappedValue.
  • 여러 개의 구조체로 간단한 접근이 필요한 경우나 (일반적으로 잘 일어나지 않는) 다른propertywrapper에서도 데이터를 감싸는 경우subscript를 압도적으로 유창하게 쓴다.
  • 다만, subscript 방법을 사용하거나, 과부하를 위해 불필요한 매개 변수를 설정하는 등 다소 헛수고를 한 부분도 있는데, 항상 그런 압도적인 이점을 선택해야 한다는 것은 아니다.

    웃기다


    (가공 인용) 단지 보기를 실현하기 위해 extension을 추가하는 것이 좋지 않습니까?
    나는 만약 적절하게 방문 수식자를 주면 큰 문제가 되지 않을 것이라고 생각한다.코딩 규약 등을 염두에 두면SearchView3 방식이 좋다.
    (허구 인용) 원래 이렇게 하면 편하겠죠?
    struct 検索条件 {
       var タイプ: タイプ
       var 中身: String
    
       enum タイプ {
           case 完全一致, 前方一致, 部分一致
       }
    }
    
    현실에서는 결코 그렇게 순조롭지 않다. 예를 들면 내용이 각기 다르다
    case 完全一致(完全一致データ)
    case 前方一致(前方一致データ)
    case 部分一致(部分一致データ)
    
    와 같은 유형에 따라 각자text 속성을 가진 장면이 있다.데이터 형식의 변경으로 효과적인 장면에 대응하는 데는 한계가 있지만 이번 설명 방법은 일반적으로 사용된다.

    끝맺다


    정의 부분은 좀 번거롭지만 subscript를 사용하면 귀속을 스마트하게 쓸 수 있습니다.기분 좋네.

    좋은 웹페이지 즐겨찾기