Как заменить TabView при клике на кнопку (SwiftUI)?
У меня есть NavigationView с EditButton(). При нажатии на копку edit, TabView должен заменится (с NormalBar на EditBar). Попробовал сделать глобальную переменную editTabBar, и смотреть её значение перед отрисовкой, но при изменении этой переменной, switch editTabBar никак не реагировал. Поставил его по умолчанию true, и понял, что даже если бы он реагировал, то не особо помогло, так как RedView() пропадает, если editTabBar == true.
Инициализация:
var body: some View {
switch editTabBar {
case false:
return AnyView( NormalBar())
case true:
return AnyView( EditBar())
}
}
TabViews:
struct NormalBar:View{
var body: some View {
TabView {
RedView()
.tabItem {
Image(systemName: "phone.fill")
Text("First Tab")
}
BlueView()
.tabItem {
Image(systemName: "tv.fill")
Text("Second Tab")
}
}
}
}
struct EditBar:View {
var body: some View {
TabView {
tabItem {
Image(systemName: "phone.fill")
Text("select all")
}
tabItem {
Image(systemName: "tv.fill")
Text("delete all")
}
}
}
}
RedView:
struct RedView: View {
@State private var items = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
@State var selectKeeper = Set<String>()
@State private var editMode = EditMode.inactive
private static var count = 0
var body: some View {
NavigationView {
List (items, id: \.self, selection: $selectKeeper){ name in
Text(name)
}.navigationBarTitle(Text("Update"), displayMode: .inline)
.navigationBarItems(leading: EditButton(), trailing: addButton)
.environment(\.editMode, $editMode)
}
}
func onAdd() {
items.append("Item #\(Self.count)")
Self.count += 1
}
private var addButton: some View {
switch editMode {
case .inactive:
editTabBar = false
return AnyView(Button(action: onAdd) { Image(systemName: "plus") })
default:
editTabBar = true
return AnyView(EmptyView())
}
}
private func onDelete(offsets: IndexSet) {
items.remove(atOffsets: offsets)
}
}
При нажатии на копку edit, TabView должен меняться, как в телеграмм, во вкладке "чаты", например. Или полная замена TabView это плохая идея и всегда будет сопровождаться исчезновением NavigationView? Тогда может скрыть этот tabView, а на его месте отрисовать кастомный элемент? Как лучше сделать?
Ответы (1 шт):
Я переписал ваш пример, изменив следующее.
- добавил кастомную панель с вкладками, так как
TabViewпредназначено для переключения вью, а не для выполнения каких-то действий по нажатию (в данном случае действия в режиме редактирования) - добавил кастомную кнопку редактирования, так как
EditButtonопять же не позволяет навесить кастомные нажатия - добавил раздельную логику для верхней панели редактирования и переключения нижней панели
- для переключения используются биндинги
struct ContentView: View {
@State var redViewSelected = true
@State var editTabBar = false
var body: some View {
VStack {
if redViewSelected {
RedView(editTabBar: $editTabBar)
} else {
BlueView()
}
if editTabBar {
EditBar()
} else {
NormalBar(redViewSelected: $redViewSelected)
}
}
}
// MARK: Bars
struct NormalBar: View {
@Binding var redViewSelected: Bool
var body: some View {
HStack {
Button(action: {
self.redViewSelected = true
}, label: {
VStack(spacing: 4) {
Image(systemName: "phone.fill")
Text("First Tab")
}
}).frame(maxWidth: .infinity)
Button(action: {
self.redViewSelected = false
}, label: {
VStack(spacing: 4) {
Image(systemName: "tv.fill")
Text("Second Tab")
}
}).frame(maxWidth: .infinity)
}
}
}
struct EditBar: View {
var body: some View {
HStack {
Button(action: {
// TODO: implement
}, label: {
VStack(spacing: 4) {
Image(systemName: "phone.fill")
Text("select all")
}
}).frame(maxWidth: .infinity)
Button(action: {
// TODO: implement
}, label: {
VStack(spacing: 4) {
Image(systemName: "tv.fill")
Text("delete all")
}
}).frame(maxWidth: .infinity)
}
}
}
// MARK: Views
struct RedView: View {
@Binding var editTabBar: Bool
@State private var items = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
@State var selectKeeper = Set<String>()
@State private var editMode = EditMode.inactive
private static var count = 0
var body: some View {
NavigationView {
List (items, id: \.self, selection: $selectKeeper){ name in
Text(name)
}.navigationBarTitle(Text("Update"), displayMode: .inline)
.navigationBarItems(leading: editButton, trailing: addButton)
.environment(\.editMode, $editMode)
}
}
func onAdd() {
items.append("Item #\(Self.count)")
Self.count += 1
}
private var editButton: some View {
Button(action: {
self.editMode.toggle()
self.editTabBar.toggle()
}) {
Text(self.editMode.title)
}
}
private var addButton: some View {
switch editMode {
case .inactive:
// Causes "Modifying state during view update, this will cause undefined behavior."
// self.editTabBar = false
return AnyView(Button(action: onAdd) { Image(systemName: "plus") })
default:
// self.editTabBar = true
return AnyView(EmptyView())
}
}
private func onDelete(offsets: IndexSet) {
items.remove(atOffsets: offsets)
}
}
struct BlueView: View {
@State private var demoData = ["Phil Swanson", "Karen Gibbons", "Grant Kilman", "Wanda Green"]
@State var selectKeeper = Set<String>()
var body: some View {
NavigationView {
List(demoData, id: \.self, selection: $selectKeeper){ name in
Text(name)
}
.navigationBarItems(trailing: EditButton())
.navigationBarTitle(Text("Selection Demo \(selectKeeper.count)"))
}
}
}
}
extension EditMode {
var title: String {
self == .active ? "Done" : "Edit"
}
mutating func toggle() {
self = self == .active ? .inactive : .active
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}