在 SwiftUI 中有条件地更改 NavigationLink 目标 [英] Change NavigationLink destination conditionally in SwiftUI

查看:30
本文介绍了在 SwiftUI 中有条件地更改 NavigationLink 目标的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有列表的视图,根据我单击的列表项,我需要打开一个不同的 NavigationLink.

I have a view with a List and depending on the item of the list I have clicked on, I need to open a different NavigationLink.

我有一个行模型用于列表的一行(我说的是列表,尽管我实际上通常使用一个名为集合的定制元素,它是一个带有 HStack 和 VStack 的列表一起模拟 UICollectionView)

I have a row model for one row of the List (I'm saying List even though I'm actually usually using a custom made element called Collection, which is a List with HStack and VStack together to emulate the UICollectionView)

在行模型视图中,我可以只为 NavigationLink 指定一个目的地.

In the row model view I can only specify one destination for the NavigationLink.

我能想到的一个解决方案是获取被单击的列表项的索引,并基于该索引打开某个视图.但我无法让它工作.

A solution I can think of is getting the index of the item of the list that was clicked and opening a certain view based on that. But I was not able to get it to work.

我基本上需要列表中的每个元素打开一个不同的视图.

I basically need that each element in the List opens a different view.

感谢任何帮助!谢谢!:)

GroupDetail.swift

GroupDetail.swift

import SwiftUI

// data displayed in the collection view
var itemsGroupData = [
  ItemsGroupModel("Projects"), // should open ProjectsView()
  ItemsGroupModel("People"), //should open PeopleView()
  ItemsGroupModel("Agenda"), //should open AgendaView()
  ItemsGroupModel("Name") //should open NameView()
]

// main view where the collection view is shown
struct GroupDetail: View {

  var body: some View {

    // FAMOUS COLLECTION ELEMENT
    Collection(itemsGroupData, columns: 2, scrollIndicators: false) { index in
      ItemsGroupRow(data: index)
    }
  }
}

// model of the data
struct ItemsGroupModel: Identifiable {
  var id: UUID
  let title: String

  init(_ title: String) {
    self.id = UUID()
    self.title = title
  }
}

// row type of the collection view
struct ItemsGroupRow: View {

  var body: some View {

    // This model is for one row
    // I need to specify what view to open depending on what item of the collection view was selected (clicked on)
    NavigationLink(destination: ProjectsView()) { // open ProjectsView only if the user clicked on the item "Projects" of the list etc..
      Text(data.title)
    }
  }
}

-----------------------------------------------------------------------

Collection.swift

// this custom struct creates the UIKit equivalent of the UICollectionView
// it uses a HStack and a VStack to make columns and rows

import SwiftUI

@available(iOS 13.0, OSX 10.15, *)
public struct Collection<Data, Content>: View
where Data: RandomAccessCollection, Content: View, Data.Element: Identifiable {

  private struct CollectionIndex: Identifiable { var id: Int }

  // MARK: - STORED PROPERTIES
  private let columns: Int
  private let columnsInLandscape: Int
  private let vSpacing: CGFloat
  private let hSpacing: CGFloat
  private let vPadding: CGFloat
  private let hPadding: CGFloat
  private let scrollIndicators: Bool
  private let axisSet: Axis.Set

  private let data: [Data.Element]
  private let content: (Data.Element) -> Content

  // MARK: - COMPUTED PROPERTIES
  private var actualRows: Int {
    return data.count / self.actualColumns
  }

  private var actualColumns: Int {
    return UIDevice.current.orientation.isLandscape ? columnsInLandscape : columns
  }

  // MARK: - INIT
  public init(_ data: Data,
              columns: Int = 2,
              columnsInLandscape: Int? = nil,
              scrollIndicators: Bool = true,
              axisSet: Axis.Set = .vertical,
              vSpacing: CGFloat = 10,
              hSpacing: CGFloat = 10,
              vPadding: CGFloat = 10,
              hPadding: CGFloat = 10,
              content: @escaping (Data.Element) -> Content) {
    self.data = data.map { $0 }
    self.content = content
    self.columns = max(1, columns)
    self.columnsInLandscape = columnsInLandscape ?? max(1, columns)
    self.vSpacing = vSpacing
    self.hSpacing = hSpacing
    self.vPadding = vPadding
    self.hPadding = hPadding
    self.scrollIndicators = scrollIndicators
    self.axisSet = axisSet
  }

  // MARK: - BODY
  public var body : some View {

    GeometryReader { geometry in
      ScrollView(self.axisSet, showsIndicators: self.scrollIndicators) {
        if self.axisSet == .horizontal {
          HStack(alignment: .center, spacing: self.hSpacing) {
            ForEach((0 ..< self.actualRows).map { CollectionIndex(id: $0) }) { row in
              self.createRow(row.id, geometry: geometry)
            }
          }
        } else {
          VStack(spacing: self.vSpacing) {
            ForEach((0 ..< self.actualRows).map { CollectionIndex(id: $0) }) { row in
              self.createRow(row.id * self.actualColumns, geometry: geometry)
            }
            // LAST ROW HANDLING
            if (self.data.count % self.actualColumns > 0) {
              self.createRow(self.actualRows * self.actualColumns, geometry: geometry, isLastRow: true)
                .padding(.bottom, self.vPadding)
            }
          }
        }
      }
    }
  }

  // MARK: - HELPER FUNCTIONS
  private func createRow(_ index: Int, geometry: GeometryProxy, isLastRow: Bool = false) -> some View {
    HStack(spacing: self.hSpacing) {
      ForEach((0 ..< actualColumns).map { CollectionIndex(id: $0) }) { column in
        self.contentAtIndex(index + column.id)
          .frame(width: self.contentWidthForGeometry(geometry))
          .opacity(!isLastRow || column.id < self.data.count % self.actualColumns ? 1.0 : 0.0)
      }
    }
  }

  private func contentAtIndex(_ index: Int) -> Content {
    // (Addressing the workaround with transparent content in the last row) :
    let object = index < data.count ? data[index] : data[data.count - 1]
    return content(object)
  }

  private func contentWidthForGeometry(_ geometry: GeometryProxy) -> CGFloat {
    let hSpacings = hSpacing * (CGFloat(self.actualColumns) - 1)
    let width = geometry.size.width - hSpacings - hPadding * 2
    return width / CGFloat(self.actualColumns)
  }
}

推荐答案

我假设您想查看元素并确定要显示的视图,以下实现了这一点.您也可以为此使用枚举.

I assume you want to look at the element and to determine what view to show, the following achieves that. You can also use an enum for this.

Collection 传入元素,因此您可以使用它和条件:

The Collection passes in the element, so you can use that and a conditional:

struct ProjectView: View {
    var row: ItemsGroupModel
    var body: some View {
        Text("Project title = \(row.title)")
    }
}

struct NonProjectView: View {
    var row: ItemsGroupModel
    var body: some View {
        Text("Non-Project title = \(row.title)")
    }
}

// row type of the collection view
struct ItemsGroupRow: View {

    var data: ItemsGroupModel

    var body: some View {

        // This model is for one row
        // I need to specify what view to open depending on what item of the collection view was selected (clicked on)
        NavigationLink(destination: {
            VStack{
                if data.title.contains("project") {
                    ProjectView(row: data)
                } else {
                    NonProjectView(row: data)
                }
            }
        }()) { // open ProjectsView only if the user clicked on the item "Projects" of the list etc..
            Text(data.title)
        }
    }
}

注意:我认为该集合确实存在一些错误.

Note: I think the collection does have some bugs.

使用无法正常工作的 enum 的示例:

Example using enum which does not work correctly:

import SwiftUI

struct ConditionalNavigationLinkView: View {
    var body: some View {
        GroupDetail()
    }
}

// data displayed in the collection view
var itemsGroupData = [
    ItemsGroupModel(.project), // should open ProjectsView()
    ItemsGroupModel(.people), //should open PeopleView()
    ItemsGroupModel(.agenda), //should open AgendaView()
    ItemsGroupModel(.name) //should open NameView()
]

// main view where the collection view is shown
struct GroupDetail: View {

    var body: some View {

        NavigationView{
            // FAMOUS COLLECTION ELEMENT
            Collection(itemsGroupData, columns: 2, scrollIndicators: false) { row in
                ItemsGroupRow(data: row)
            }
        }
    }
}

enum ItemsGroupModelType: String {
    case project = "Projects"
    case people = "People"
    case agenda = "Agenda"
    case name = "Name"
}

// model of the data
struct ItemsGroupModel: Identifiable {
    var id: UUID
    let type: ItemsGroupModelType

    init(_ type: ItemsGroupModelType) {
        self.id = UUID()
        self.type = type
    }
}

struct ProjectView: View {
    var row: ItemsGroupModel
    var body: some View {
        Text("Projects \(row.type.rawValue)")
    }
}

struct NameView: View {
    var row: ItemsGroupModel
    var body: some View {
        Text("NameView \(row.type.rawValue)")
    }
}

struct PeopleView: View {
    var row: ItemsGroupModel
    var body: some View {
        Text("PeopleView \(row.type.rawValue)")
    }
}

struct AgendaView: View {
    var row: ItemsGroupModel
    var body: some View {
        Text("AgendaView \(row.type.rawValue)")
    }
}

// row type of the collection view
struct ItemsGroupRow: View {

    func printingEmptyView() -> EmptyView {
        print("type: \(data.type.rawValue)")
        return EmptyView()
    }

    var data: ItemsGroupModel

    var body: some View {

        // This model is for one row
        // I need to specify what view to open depending on what item of the collection view was selected (clicked on)
        NavigationLink(destination: {
            //print("Hello")

            VStack{
                self.printingEmptyView()
                if data.type == .project {
                    ProjectView(row: data)
                }
                if data.type == .people {
                    PeopleView(row: data)
                }
                if data.type == .agenda {
                    AgendaView(row: data)
                }
                if data.type == .name {
                    NameView(row: data)
                }
            }
        }()) { // open ProjectsView only if the user clicked on the item "Projects" of the list etc..
            Text(data.type.rawValue)
        }
    }
}

这篇关于在 SwiftUI 中有条件地更改 NavigationLink 目标的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆