MenuItem
というveryカスタムNavigationLinkを実装しています。プロジェクト全体で再利用したいと考えています。これは、View
に準拠し、NavigationLink
を含むvar body : some View
を実装する構造体です。 NavigationLink
によって提示されるビューをMenuItem
の本文になんとかして保存する必要がありますが、まだ保存できていません。
destinationView
の本文でMenuItem
をsome 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がいくつかのビューを引数として受け入れることができる方法があるはずです。ありがとう
方法Appleは関数ビルダーを使用していますか、ViewBuilderと呼ばれる事前定義されたものがあります。MenuItemのinitメソッドの最後の引数、または唯一の引数をこれにします
...., @ViewBuilder builder: @escaping () -> Content)
次のように定義されたプロパティに割り当てます
let viewBuilder: () -> Content
次に、ビューを出力する場所で、次のような関数を呼び出します
HStack {
viewBuilder()
}
その後、あなたはできる
MenuItem {
Image("myImage")
Text("My Text")
}
これにより、最大10個のビューを渡して、if条件などを使用できますが、より制限したい場合は、独自の関数ビルダーを定義する必要があります。私はそれをしていませんので、ググる必要があります。
ジェネリックパラメーターをMenuItem
の一部にする必要があります。
struct MenuItem<Content: View>: View {
private var destinationView: Content
init(destinationView: Content) {
self.destinationView = destinationView
}
var body : some View {
// ...
}
}
次のようなカスタムビューを作成できます。
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()
}
}
私は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()
}
}
}
}
}
次のように、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")
}
}