SwiftUI工作表:工作表行为与navigationBarItems不一致

时间:2019-12-26 23:30:48

标签: ios swift swiftui

Xcode:版本11.3(11C29)
目标:运行iOS 13.3的模拟器

使用表格和SwiftUI时出现不一致的行为,很难诊断。当我:
 *使用NavigationView本身中的按钮,我可以始终调出工作表并将其关闭
 *使用NavigationView(即navigationBarItems)栏中的按钮,行为不一致:有时会切换而不会出现问题,有时会“锁定”并且在运行前约10秒钟没有响应再次。在界面中四处滑动并执行其他操作似乎有助于“重置”功能。

最小的可复制示例:

import SwiftUI

struct ContentView: View {
    @State var isModalShowing = false

    var body: some View {
        NavigationView {
            VStack {
                List {
                    ForEach(1..<5) { number in
                        Text(String(number))
                    }
                }
                Button("First Modal Button") {
                    self.isModalShowing = true
                }
            }
            .navigationBarItems(leading:
                Button("Second Modal Button") {
                    self.isModalShowing = true
            })
        }
        .sheet(isPresented: $isModalShowing) {
            TestView(isPresented: self.$isModalShowing)
        }
    }
}

struct TestView: View {
    @Binding var isPresented: Bool
    var body: some View {
        VStack {
            Text("Hello SwiftUI!")
            Button("Dismiss") {
                self.isPresented = false
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

注意:
*连续按First modal button即可
*首次关闭模态后,按Second modal button会产生不一致的效果(尝试连续约5-10次使用此按钮,它应该会锁定,但是很难预测何时会出现)

已经有a few个StackOverflow问题/ answers about this,但是到目前为止,没有一个问题对我有用,他们似乎在谈论Xcode Beta,而我现在还很稳定(希望11.3是稳定的)。传递状态的不同方式(@Binding@Environment)无济于事。在.sheet被调用的地方移动也没有任何改变。我认为它也与List和/或ForEach有关,但是我是Swift的新手,不确定如何进一步调试。

5 个答案:

答案 0 :(得分:1)

我相信这是一个错误。尽管我的答案不会解决这个问题,但是可以给您选择的机会。

出于好奇,我对视图层次结构进行了一些调试。在当前设置下,导航栏按钮最初位于此处(突出显示为蓝色):

enter image description here

然后,您需要提交纸页并相应地解散纸页。解雇后,导航栏按钮的位置不太正确。参见:

enter image description here

按钮的Text保持在正确的位置,但是条形按钮从应有的位置移开了。

但是,如果您使用NavigationLink而非.sheet演示文稿,则不会发生这种移位。

好吧,此问题在导航栏标题的large的{​​{1}}选项中很普遍,并且很长一段时间以来都是默认设置。但是,如果使用TitleDisplayMode选项,则该问题不存在。使用inline选项,在解雇工作表前后,请注意导航栏按钮将停留在同一位置:

enter image description here


因此,您现在可以考虑两个选项:

  1. 使用inline代替工作表演示。您可以通过将以下代码放在最外面的NavigationLink(在您的情况下为View)中来实现此目的:

    VStack
  

注意:此NavigationLink(destination: TestView(), isActive: self.$isModalShowing) { EmptyView() // no need to provide any other view as it will be triggered by the action // of navigation bar button item which already provides its own view } 选项未在 Xcode 11.3 或更高版本上进行过测试,因为此版本的导航链接似乎不正常。但可以正常使用,直到 Xcode 11.2.1 。有关SwiftUI unable to navigate back and forth with navigationLink

的更多信息
  1. 将导航栏NavigationLink的{​​{1}}选项用作:

    inline

答案 1 :(得分:0)

解散模态表的另一种方法:

struct TestView: View {
    @Environment(\.presentationMode) var presentationMode

    var body: some View {
        VStack {
            Text("Hello SwiftUI!")
            Button("Dismiss") {
                self.presentationMode.wrappedValue.dismiss()
            }
        }
    }
}

答案 2 :(得分:0)

这里没有一个解决方案对我有用,但是这个解决方案确实可以-下拉到UIKit来使用另一个dismiss函数:What is different between @Binding and scroll down to dismiss presented view?

答案 3 :(得分:0)

通过将文本视图与onTapGesture结合使用,有一种解决方法,但是,点击该按钮时,您将失去按钮颜​​色的动画。

workaround

代码类似于:

.navigationBarItems(leading:
    Text("Text")
        .onTapGesture {
            self.isPresentingSheet = true
        }
        .sheet(isPresented: self.$isPresentingSheet, content: {
            SheetView()
        })
)

答案 4 :(得分:0)

我找到了一种替代方法来解决此问题,同时仍使用按钮和大标题。这是代码:

struct ContentView: View {

    @State var isModalShowing = false
    @State var opacity: Double = 1

    var body: some View {
        NavigationView() {

            List() {

                // Some views here

            }
            .navigationBarTitle(Text("Home"), displayMode: .automatic)
            .navigationBarItems(trailing: 

                Button(action: {

                    self.opacity = 0.5
                    self.isModalShowing = true

                }, label: {

                    Image(systemName: "plus")

                })

                    .opacity(self.opacity)
                    .scaleEffect(1.5)
                    .sheet(isPresented: $isModalShowing,
                           onDismiss: {
                               self.opacity = 1
                        },    
                           content: {

                               NewView(onDismiss: self.$isModalShowing)

                        })
                )
           }

      }
}

基本上,我们在点击按钮时更改不透明度,而在关闭工作表时将其更改为不透明度,我认为这将迫使按钮自行更新。

关于解散纸张,在这种情况下,OP的工作方式有效