web-dev-qa-db-ja.com

1つのSwiftUIビューを変数として別のビュー構造体に渡す方法

MenuItemというveryカスタムNavigationLinkを実装しています。プロジェクト全体で再利用したいと考えています。これは、Viewに準拠し、NavigationLinkを含むvar body : some Viewを実装する構造体です。 NavigationLinkによって提示されるビューをMenuItemの本文になんとかして保存する必要がありますが、まだ保存できていません。

destinationViewの本文でMenuItemsome Viewとして定義し、2つの初期化子を試しました。

これは簡単すぎるように見えました。

struct MenuItem: View {
    private var destinationView: some View

    init(destinationView: View) {
        self.destinationView = destinationView
    }

    var body : some View {
        // Here I'm passing destinationView to NavigationLink...
    }
}

->エラー:プロトコル 'View'は、Selfまたは関連する型の要件があるため、一般的な制約としてのみ使用できます。

2回目の試行:

struct MenuItem: View {
    private var destinationView: some View

    init<V>(destinationView: V) where V: View {
        self.destinationView = destinationView
    }

    var body : some View {
        // Here I'm passing destinationView to NavigationLink...
    }
}

->エラー:タイプ「V」の値をタイプ「some View」に割り当てることはできません。

最後の試み:

struct MenuItem: View {
    private var destinationView: some View

    init<V>(destinationView: V) where V: View {
        self.destinationView = destinationView as View
    }

    var body : some View {
        // Here I'm passing destinationView to NavigationLink...
    }
}

->エラー:タイプ 'View'の値をタイプ 'some View'に割り当てることができません。

誰かが私を助けてくれることを願っています。 NavigationLinkがいくつかのビューを引数として受け入れることができる方法があるはずです。ありがとう

18
Alex

方法Appleは関数ビルダーを使用していますか、ViewBuilderと呼ばれる事前定義されたものがあります。MenuItemのinitメソッドの最後の引数、または唯一の引数をこれにします

...., @ViewBuilder builder: @escaping () -> Content)

次のように定義されたプロパティに割り当てます

  let                 viewBuilder: () -> Content

次に、ビューを出力する場所で、次のような関数を呼び出します

HStack {
    viewBuilder()
}

その後、あなたはできる

MenuItem {
   Image("myImage")
   Text("My Text")
}

これにより、最大10個のビューを渡して、if条件などを使用できますが、より制限したい場合は、独自の関数ビルダーを定義する必要があります。私はそれをしていませんので、ググる必要があります。

11
Nathan Day

ジェネリックパラメーターをMenuItemの一部にする必要があります。

struct MenuItem<Content: View>: View {
    private var destinationView: Content

    init(destinationView: Content) {
        self.destinationView = destinationView
    }

    var body : some View {
        // ...
    }
}
13
rraphael

次のようなカスタムビューを作成できます。

struct ENavigationView<Content: View>: View {

    let viewBuilder: () -> Content

    var body: some View {
        NavigationView {
            VStack {
                viewBuilder()
                    .navigationBarTitle("My App")
            }
        }
    }

}

struct ENavigationView_Previews: PreviewProvider {
    static var previews: some View {
        ENavigationView {
            Text("Preview")
        }
    }
}

使用:

struct ContentView: View {

    var body: some View {
        ENavigationView {
            Text("My Text")
        }
    }

}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
8

私はViewの拡張のために私のものを機能させるのに本当に苦労しました。呼び出し方法の詳細は、 here を参照してください。

Viewの拡張(ジェネリックを使用)-import SwiftUI

extension View {

    /// Navigate to a new view.
    /// - Parameters:
    ///   - view: View to navigate to.
    ///   - binding: Only navigates when this condition is `true`.
    func navigate<SomeView: View>(to view: SomeView, when binding: Binding<Bool>) -> some View {
        modifier(NavigateModifier(destination: view, binding: binding))
    }
}


// MARK: - NavigateModifier
fileprivate struct NavigateModifier<SomeView: View>: ViewModifier {

    // MARK: Private properties
    fileprivate let destination: SomeView
    @Binding fileprivate var binding: Bool


    // MARK: - View body
    fileprivate func body(content: Content) -> some View {
        NavigationView {
            ZStack {
                content
                    .navigationBarTitle("")
                    .navigationBarHidden(true)
                NavigationLink(destination: destination
                    .navigationBarTitle("")
                    .navigationBarHidden(true),
                               isActive: $binding) {
                    EmptyView()
                }
            }
        }
    }
}
1
George_E

次のように、NavigationLink(またはその他のビューウィジェット)を変数としてサブビューに渡すことができます。

import SwiftUI

struct ParentView: View {
    var body: some View {
        NavigationView{

            VStack(spacing: 8){

                ChildView(destinationView: Text("View1"), title: "1st")
                ChildView(destinationView: Text("View2"), title: "2nd")
                ChildView(destinationView: ThirdView(), title: "3rd")
                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)
        }
    }
}

struct ThirdView: View {
    var body: some View {
        VStack(spacing: 8){

            ChildView(destinationView: Text("View1"), title: "1st")
            ChildView(destinationView: Text("View2"), title: "2nd")
            ChildView(destinationView: ThirdView(), title: "3rd")
            Spacer()
        }
        .padding(.all)
        .navigationBarTitle("NavigationLinks")
    }
}
1
Yarm