#ios #swift #swiftui #swiftui-list
#iOS #swift #swiftui #swiftui-список
Вопрос:
У меня есть представление SwiftUI, которое состоит из списка с некоторыми элементами. Некоторые из них являются ссылками на другие экраны (поэтому я использую NavigationLink
для этого), а другие — действия, которые я хочу выполнить на текущем экране (например, кнопка для отображения таблицы действий).
Я ищу способ отображения a Button
в SwiftUI List
с индикатором раскрытия (шеврон на правом знаке, который показан для NavigationLink
).
Возможно ли это?
Например.
struct ExampleView: View {
@State private var showingActionSheet = false
var body: some View {
NavigationView {
List {
NavigationLink("Navigation Link", destination: Text("xx"))
Button("Action Sheet") {
self.showingActionSheet = true
}
.foregroundColor(.black)
}
.listStyle(GroupedListStyle())
.actionSheet(isPresented: $showingActionSheet) {
ActionSheet(title: Text("Title"), buttons: [
.default(Text("Do Something")) { },
.cancel()
])
}
}
}
}
Текущее поведение:
Требуемое поведение:
Ответ №1:
В моем ответе используется фреймворк SwiftUI-Introspect, используемый для:
Проанализируйте базовые компоненты UIKit от SwiftUI
В этом случае она используется для отмены выбора строки после NavigationLink
нажатия.
Я бы подумал, что кнопка с обычным цветом акцента и без навигационной ссылки будет более интуитивно понятной для пользователя, но если это то, что вам нужно, вот она. Следующий ответ должен работать для вас:
import Introspect
import SwiftUI
struct ExampleView: View {
@State private var showingActionSheet = false
@State private var tableView: UITableView?
var body: some View {
NavigationView {
List {
NavigationLink("Navigation Link", destination: Text("xx"))
NavigationLink(
destination: EmptyView(),
isActive: Binding<Bool>(
get: { false },
set: { _ in
showingActionSheet = true
DispatchQueue.main.async {
deselectRows()
}
}
)
) {
Text("Action Sheet")
}
}
.introspectTableView { tableView = $0 }
.listStyle(GroupedListStyle())
.actionSheet(isPresented: $showingActionSheet) {
ActionSheet(title: Text("Title"), buttons: [
.default(Text("Do Something")) { },
.cancel()
])
}
}
}
private func deselectRows() {
if let tableView = tableView, let selectedRow = tableView.indexPathForSelectedRow {
tableView.deselectRow(at: selectedRow, animated: true)
}
}
}
Комментарии:
1. Вот как я реализовал внутри раздела в представлении списка . Я думаю, это похоже на то, что предложил @George выше.
HStack { Button { showPopover.toggle() } label: { NavigationLink(destination: {}, label: { Text("Version Number") Spacer() Text("1.1.0") })
.
Ответ №2:
Возможный подход заключается в создании строки с пользовательским шевроном, как в демонстрации ниже (протестировано с Xcode 12.1 / iOS 14.1)
struct ExampleView: View {
@State private var showingActionSheet = false
var body: some View {
NavigationView {
List {
HStack {
Text("Navigation Link")
// need to hide navigation link to use same chevrons
// because default one is different
NavigationLink(destination: Text("xx")) { EmptyView() }
Image(systemName: "chevron.right")
.foregroundColor(Color.gray)
}
HStack {
Button("Action Sheet") {
self.showingActionSheet = true
}
.foregroundColor(.black)
Spacer()
Image(systemName: "chevron.right")
.foregroundColor(Color.gray)
}
}
.listStyle(GroupedListStyle())
.actionSheet(isPresented: $showingActionSheet) {
ActionSheet(title: Text("Title"), buttons: [
.default(Text("Do Something")) { },
.cancel()
])
}
}
}
}
Ответ №3:
Я использовал ZStack, чтобы поместить a NavigationLink
с пустой меткой за фактическим содержимым метки. Таким образом, вы получите правильный символ шеврона. Для isActive
привязки вы можете просто использовать .constant(false)
привязку, которая всегда будет возвращать false и не может быть изменена.
В результате появится строка, которая выглядит точно так же, как навигационная ссылка, но ведет себя как кнопка. К сожалению, это также включает в себя недостаток, заключающийся в том, что пользователь должен щелкнуть по содержимому ярлыка (тексту), чтобы активировать кнопку, и не может просто щелкнуть по пустому месту ячейки.
struct ExampleView: View {
@State private var showingActionSheet = false
var body: some View {
NavigationView {
List {
NavigationLink("Navigation Link", destination: Text("xx"))
Button {
self.showingActionSheet = true
} label: {
// Put a NavigationLink behind the actual label for the chevron
ZStack(alignment: .leading) {
// NavigationLink that can never be activated
NavigationLink(
isActive: .constant(false),
destination: { EmptyView() },
label: { EmptyView() }
)
// Actual label content
Text("Action Sheet")
}
}
// Prevent the blue button tint
.buttonStyle(.plain)
}
.listStyle(GroupedListStyle())
.actionSheet(isPresented: $showingActionSheet) {
ActionSheet(title: Text("Title"), buttons: [
.default(Text("Do Something")) { },
.cancel()
])
}
}
}
}
Ответ №4:
struct NavigationButton: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
ZStack(alignment: .leading) {
NavigationLink(
isActive: .constant(false),
destination: { EmptyView() },
label: { EmptyView() }
)
configuration.label
}
}
}
Пример:
NavigationView {
List {
Button(action: {
openURL(URL(string: "https://www.apple.com")!)
}) {
Text("Visit Apple")
}
.buttonStyle(NavigationButton())
}
}