簡體   English   中英

SwiftUI 列出不帶 NavigationLink 的披露指示器

[英]SwiftUI List disclosure indicator without NavigationLink

我正在尋找一種解決方案來顯示披露指示符 chevron 而無需將我的視圖包裝到NavigationLink中。 例如,我想顯示指示器但不導航到新視圖,而是顯示模式。

我找到了很多隱藏指示器按鈕的解決方案,但沒有一個可以解釋如何添加一個。 在當前的 SwiftUI 版本中,這甚至可能嗎?

struct MyList: View {
    var body: some View {
        NavigationView {
        List {
            Section {
                Text("Item 1")
                Text("Item 2")
                Text("Item 3")
                Text("Item 4")

            }
        }
    }
}

例如,我想將披露指示符添加到Item 1 ,而無需將其包裝到NavigationLink

我已經嘗試使用chevron.right SF 符號來偽造指標,但該符號與默認的 iOS 符號 100% 不匹配。 頂部是默認底部是chevron.right

披露按鈕圖像

希望這是您正在尋找的。 您可以將項目添加到 HStack 並在之間使用 Spacer 假裝它是一個鏈接:

HStack {
                    Text("Item 1")
                    Spacer()
                    Button(action: {

                    }){
                        Image(systemName: "chevron.right")
                            .font(.body)
                    }
                }

這絕對是可能的。

您可以使用Button和非功能NavigationLink的組合來實現您想要的。

NavigationLink上添加以下擴展。

extension NavigationLink where Label == EmptyView, Destination == EmptyView {

   /// Useful in cases where a `NavigationLink` is needed but there should not be
   /// a destination. e.g. for programmatic navigation.
   static var empty: NavigationLink {
       self.init(destination: EmptyView(), label: { EmptyView() })
   }
}

然后,在您的List中,您可以對行執行以下操作:

// ...
ForEach(section.items) { item in
    Button(action: {
        // your custom navigation / action goes here
    }) {
        HStack {
            Text(item.name)
            Spacer()
            NavigationLink.empty
        }
    }
 }
 // ...

以上產生的結果與您使用NavigationLink相同,並且還按預期在交互中突出顯示/取消突出顯示該行。

在 iOS15 中,以下是更好的匹配,因為其他解決方案有點太大而且不夠大膽。 與指定字體大小相比,它還可以更好地調整到不同的顯示比例。

HStack {
    Text("Label")
    Spacer()
    Image(systemName: "chevron.forward")
      .font(Font.system(.caption).weight(.bold))
      .foregroundColor(Color(UIColor.tertiaryLabel))
}

如果有一種官方的方式來做到這一點會很好。 更新每個操作系統調整很煩人。

或者也許創建一個假的並使用它,即使你點擊你也可以調用你的事件。

 NavigationLink(destination: EmptyView()) {
            HStack {
                Circle()
                 Text("TITLE")  
               
            }
        }
        .contentShape(Rectangle())
        .onTapGesture {
            print("ALERT MAYBE")
        }

我找到了一個原始的解決方案。 手動插入圖標不會帶來完全相同的外觀。

訣竅是使用帶有“isActive”參數的初始化程序並傳遞一個始終為假的本地綁定。 所以 NavigationLink 等待一個永遠不會發生的以編程方式觸發的事件。

// use this initializer
NavigationLink(isActive: <Binding<Bool>>, destination: <() -> _>, label: <() -> _>)

您可以將空閉包傳遞給目標參數。 無論如何,它永遠不會被調用。 要執行一些操作,您可以在 ZStack 的頂部放置一個按鈕。

func navigationLinkStyle() -> some View {
    let never = Binding<Bool> { false } set: { _ in }
    return ZStack {
        NavigationLink(isActive: never, destination: { }) {
            Text("Item 1")  // your list cell view
        }
        Button {
           // do your action on tap gesture
        } label: {
            EmptyView()  // invisible placeholder
        }
    }
}

對於可訪問性,您可能需要模仿UIKit版本的披露指示器。 您本身不需要以這種方式實現它,但如果您使用例如 Appium 進行測試,您可能希望這樣來保持測試成功

顯然UIKit的披露指示器是一個禁用按鈕,具有一些可訪問性值,所以這里是解決方案:

struct DisclosureIndicator: View {
    var body: some View {
        Button {

        } label: {
            Image(systemName: "chevron.right")
                .font(.body)
                .foregroundColor(Color(UIColor.tertiaryLabel))
        }
        .disabled(true)
        .accessibilityLabel(Text("chevron"))
        .accessibilityIdentifier("chevron")
        .accessibilityHidden(true)
    }
}

已經提交的答案並沒有說明一件事:點擊時單元格的突出顯示。 請參閱我的答案底部圖像中的About Peek-a-View單元格 - 它被突出顯示,因為我在截取屏幕截圖時按下了它。

我的解決方案同時考慮了這一點雪佛龍:

Button(action: { /* handle the tap here */ }) {
    NavigationLink("Cell title", destination: EmptyView())
}
.foregroundColor(Color(uiColor: .label))

當單元被點擊時, Button的存在似乎會通知 SwiftUI; 僅僅添加一個onTapGesture()是不夠的。

這種方法的唯一缺點是需要指定.foregroundColor() 沒有它,按鈕文本將改為藍色。

突出顯示一個單元格的 SwiftUI 列表的屏幕截圖

我創建了一個自定義NavigationLink

  1. 添加一個action API (而不是必須推送一個View
  2. 顯示披露指標
  3. 確保List單元格選擇保持原樣

用法

MYNavigationLink(action: {
  didSelectCell()
}) {
  MYCellView()
}

代碼

import SwiftUI

struct MYNavigationLink<Label: View>: View {
  
  @Environment(\.colorScheme) var colorScheme
  
  private let action: () -> Void
  private let label: () -> Label
  
  init(action: @escaping () -> Void, @ViewBuilder label: @escaping () -> Label) {
    self.action = action
    self.label = label
  }
  
  var body: some View {
    Button(action: action) {
      HStack(spacing: 0) {
        label()
        Spacer()
        NavigationLink.empty
          .layoutPriority(-1) // prioritize `label`
      }
    }
    // Fix the `tint` color that `Button` adds
    .tint(colorScheme == .dark ? .white : .black) // TODO: Change this for your app
  }
}

// Inspiration:
// - https://stackoverflow.com/a/66891173/826435
private extension NavigationLink where Label == EmptyView, Destination == EmptyView {
   static var empty: NavigationLink {
       self.init(destination: EmptyView(), label: { EmptyView() })
   }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM