简体   繁体   English

如何在 SwiftUI 中将 NavigationLink 显示为按钮

[英]How to show NavigationLink as a button in SwiftUI

I've read a lot here about navigation in SwiftUI and tried a couple of things, but nothing is working as desired.我在 SwiftUI 中阅读了很多关于导航的文章,并尝试了一些方法,但没有任何效果符合预期。

Basically, I have a view with a list of workouts and you can show a single workout by clicking on a row.基本上,我有一个包含锻炼列表的视图,您可以通过单击一行来显示单个锻炼。 This works as expected using NavigationView together with NavigationLink.将 NavigationView 与 NavigationLink 一起使用,这会按预期工作。

Now, I want a button on the detail view to start the workout.现在,我想要详细视图上的一个按钮来开始锻炼。 This should open a view with a timer.这应该打开一个带有计时器的视图。 The view should be presented with the same animation as the detail view did and also show the name of the workout in the navigation bar with a back button.该视图应显示为与详细视图相同的 animation,并且还会在带有后退按钮的导航栏中显示锻炼名称。

I could implement this with a NavigationLink view on the details page, but the link always appears as a full width row with the arrow on the right side.我可以在详细信息页面上使用 NavigationLink 视图来实现这一点,但链接始终显示为全宽行,箭头位于右侧。 I'd like this to be a button instead, but the NavigationLink seems to be resistant against styling.我希望这是一个按钮,但 NavigationLink 似乎不适合样式。

struct WorkoutDetail: View {
    var workout: Workout

    var body: some View {
        VStack {
            NavigationLink(destination: TimerView()) {
                Text("Starten")
            }.navigationBarTitle(Text(workout.title))
        }
    }
}

struct WorkoutList: View {
    var workoutCollection: WorkoutCollection

    var body: some View {
        NavigationView {
            List(workoutCollection.workouts) { workout in
                NavigationLink(destination: WorkoutDetail(workout: workout)) {
                    WorkoutRow(workout: workout)
                }
            }.navigationBarTitle(Text("Workouts"))
        }
    }
}

Updated: Here's a screenshot to illustrate what I mean:更新:这是一个截图来说明我的意思:

当前布局:使用默认右箭头的“开始”。所需布局:黄色背景的蓝色按钮。

You don't need to wrap your view inside the NavigationLink to make it trigger the navigation when pressed.您不需要将您的视图包装在NavigationLink以使其在按下时触发导航。

We can bind a property with our NavigationLink and whenever we change that property our navigation will trigger irrespective of what action is performed.我们可以将一个属性与我们的NavigationLink绑定,每当我们更改该属性时,无论执行什么操作,我们的导航都会触发。 For example:例如:

struct SwiftUI: View {
    @State private var action: Int? = 0
    
    var body: some View {
        
        NavigationView {
            VStack {
                NavigationLink(destination: Text("Destination_1"), tag: 1, selection: $action) {
                    EmptyView()
                }
                NavigationLink(destination: Text("Destination_2"), tag: 2, selection: $action) {
                    EmptyView()
                }
                
                Text("Your Custom View 1")
                    .onTapGesture {
                        //perform some tasks if needed before opening Destination view
                        self.action = 1
                    }
                Text("Your Custom View 2")
                    .onTapGesture {
                        //perform some tasks if needed before opening Destination view
                        self.action = 2
                    }
            }
        }
    }
}

Whenever you change the value of your Bindable property (ie action) NavigationLink will compare the pre-defined value of its tag with the binded property action , if both are equal navigation takes place.每当您更改Bindable属性(即 action)的值时, NavigationLink都会将其tag的预定义值与绑定属性action ,如果两者相等,则导航发生。

Hence you can create your views any way you want and can trigger navigation from any view regardless of any action, just play with the property binded with NavigationLink .因此,您可以以任何您想要的方式创建您的视图,并且可以从任何视图触发导航而不管任何操作,只需使用与NavigationLink绑定的属性即可。

Thanks, hope it helps.谢谢,希望有帮助。

I think that the accurate way to do it is using buttonStyle , for example我认为准确的方法是使用buttonStyle ,例如

NavigationLink(destination: WorkoutDetail(workout: workout)) {
  WorkoutRow(workout: workout)
}
.buttonStyle(ButtonStyle3D(background: Color.yellow))

I've been playing around with this for a few days myself.我自己已经玩了几天了。 I think this is what you're looking for.我想这就是你要找的。

struct WorkoutDetail: View {
var workout: Workout

var body: some View {
    NavigationView {
       VStack {
           NavigationLink(destination: TimerView()) {
               ButtonView()
           }.navigationBarTitle(Text(workout.title))
        }
    }
}

And create a View you want to show in the NavigationLink并创建一个要在 NavigationLink 中显示的视图

struct ButtonView: View {
var body: some View {
    Text("Starten")
        .frame(width: 200, height: 100, alignment: .center)
        .background(Color.yellow)
        .foregroundColor(Color.red)
}

Note: Anything above the NavigationView appears to show up on all pages in the Navigation, making the size of the NavigationView Smaller in the links.注意: NavigationView 上方的任何内容似乎都会显示在 Navigation 的所有页面上,从而使 NavigationView 在链接中的大小变小。

Use the NavigationLink inside the button's label.使用按钮标签内的 NavigationLink。

Button(action: {
    print("Floating Button Click")
}, label: {
    NavigationLink(destination: AddItemView()) {
         Text("Open View")
     }
})

Very similar with Pankaj Bhalala 's solution but removed ZStack:Pankaj Bhalala的解决方案非常相似,但删除了 ZStack:

struct ContentView: View {
    @State var selectedTag: String?

    var body: some View {
        Button(action: {
            self.selectedTag = "xx"
        }, label: {
            Image(systemName: "plus")
        })
        .background(
            NavigationLink(
                destination: Text("XX"),
                tag: "xx",
                selection: $selectedTag,
                label: { EmptyView() }
            )
        )
    }
}

Wait for a more concise solution without state.等待更简洁的没有状态的解决方案。 How about you?你呢?

You can use NavigationLink as Button with ZStack:您可以将 NavigationLink 用作 ZStack 的按钮:

@State var tag:Int? = nil
ZStack {
  NavigationLink(destination: MyModal(), tag: 1, selection: $tag) {
    EmptyView()
  }

  Button(action: {
    self.tag = 1
  }, label: {
    Text("show view tag 1")
  })
}

Hope it will help.希望它会有所帮助。

I don't know why all these answers are making this so complicated.我不知道为什么所有这些答案都让事情变得如此复杂。 In SwiftUI 2.0, you just add the button inside the navigation link!在SwiftUI 2.0,你只需要添加的导航链接的按钮!

NavigationLink(destination: TimerView()) {
    Text("Starten")
}

You can apply SwiftUI styling to the Text object just as you would style any other element.您可以将 SwiftUI 样式应用到Text对象,就像设置任何其他元素的样式一样。

You can add the navigation link directly to the button like this:您可以将导航链接直接添加到按钮,如下所示:

    @State var tag:Int? = nil

    ...

    NavigationLink(destination: Text("Full List"), tag: 1, selection: $tag) {
        MyButton(buttonText: "Full list") {
            self.tag = 1
        }
    }

I changed the List to ScrollView and it worked.我将List更改为ScrollView并且它起作用了。

Xcode 11 Beta 5终于解决了这个问题,因此是一个错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM