SwiftUI 2.0 ReusableView that will contain a link as a variable

93 Views Asked by At

I am a working on an app that has a list of sports, i built a reusable view for the name and image of sports to be used to list each sport. Then i made a view for each sport to be opened when a user taps a sport image from the list of sports, i created a variable for image, sportname and link, i dont know how to link a variable with NavigationLink. I am lost here, any help will be really appreciated. Below is my code.

struct SportTypeInsetView: View {
var buttonImage1: String
var buttonName1: String
var buttonLink1: String
var body: some View {
VStack {
NavigationView {
VStack {
  Button (action: {
                        
}) {
  Image(buttonImage1)
   .resizable()
   .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
    }
     .frame(width: 50, height: 30)
                
    Text(buttonName1)
    .font(.footnote)
    .foregroundColor(.primary)
    .multilineTextAlignment(.leading)
     .lineLimit(1)
       }
      }
    }
   } 
}

SportTypeInsetView(buttonImage1: "Yoga-icon", buttonName1: "Yoga",buttonLink1: )

How do i connect it to YogaView() when the button is tapped?

Thanks.

2

There are 2 best solutions below

1
On BEST ANSWER

You can use NavigationLink with button Style DefaultButtonStyle.

NavigationLink has a label parameter, which is basically a SwiftUI view object, you can simply pass a Text object so that it looks like button or pass both Image and Text, it's up to you, end result will be button style NavigationLink.

Providing a button style helps to give you a similar feel like button.

NavigationLink(destination: SportDetailView(),
               label: {
                        Text("Navigate To next Screen")
             }).buttonStyle(DefaultButtonStyle())

In case you want to customize the NavigationLink Label View, you can do something like this.

  NavigationLink(destination: SportDetailView(),
                 label: {
                           HStack {
                               Image(systemName: "pencil")
                               Text("Edit name")
                           }
               }).buttonStyle(DefaultButtonStyle())

To achieve a Navigation link which is dynamic in behavior, SportTypeInsetView can have a generic Type TargetView which is constrained by View protocol.

With that being said you would not need buttonLink1 as the targetView itself is dynamic.

ex: 
SportTypeInsetView(targetView: YogaView(), buttonImage1: "Tap", buttonName1: "Button A", buttonLink1: "")

ex:
SportTypeInsetView(targetView: SomeAnotherSportView(), buttonImage1: "Tap", buttonName1: "Button B", buttonLink1: "")
struct SportTypeInsetView<TargetView: View>: View {

    let targetView: TargetView
    var buttonImage1: String
    var buttonName1: String
    var buttonLink1: String
    
    var body: some View {
        VStack {
            NavigationView {
                VStack {
                    NavigationLink(
                        destination: self.targetView,
                        label: {
                            //Add Image , text or button here
                            Text("Navigation to next")
                        }).buttonStyle(DefaultButtonStyle())
                    
                    
                    Text(buttonName1)
                        .font(.footnote)
                        .foregroundColor(.secondary)
                        .multilineTextAlignment(.leading)
                        .lineLimit(1)
                }
            }
        }
    }
}
1
On

The easiest way is to replace your Button with a NavigationLink. In the NavigationLink, you pass it a view (YogaView) and you can give it parameters just like any other view. Here's an example:

struct ContentView : View {
    var body: some View {
        SportTypeInsetView(buttonImage1: "0", buttonName1: "Button", buttonLink1: "Link")
    }
}

struct SportTypeInsetView: View {
    var buttonImage1: String
    var buttonName1: String
    var buttonLink1: String
    
    var body: some View {
        VStack {
            NavigationView {
                VStack {
                    NavigationLink(destination: YogaView(linkName: buttonLink1)) {
                        Image(buttonImage1)
                            .resizable()
                            .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
                            .frame(width: 50, height: 30)
                    }
                    
                    Text(buttonName1)
                        .font(.footnote)
                        .foregroundColor(.primary)
                        .multilineTextAlignment(.leading)
                        .lineLimit(1)
                }
            }
        }
    }
}

struct YogaView : View {
    var linkName : String
    
    var body: some View {
        Text(linkName)
    }
}

If you really wanted to do it with a button, you can do so with the isActive property of NavigationLink and a @State variable:

struct SportTypeInsetView: View {
    var buttonImage1: String
    var buttonName1: String
    var buttonLink1: String
    
    @State private var linkActive = false
    
    var body: some View {
        VStack {
            NavigationView {
                VStack {
                    Button(action: { linkActive = true }) {
                        Image(buttonImage1)
                            .resizable()
                            .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
                            .frame(width: 50, height: 30)
                    }
                    .overlay(
                        NavigationLink(destination: YogaView(linkName: buttonLink1), isActive: $linkActive) {
                            EmptyView()
                        })
                    
                    Text(buttonName1)
                        .font(.footnote)
                        .foregroundColor(.primary)
                        .multilineTextAlignment(.leading)
                        .lineLimit(1)
                }
            }
        }
    }
}