表单/表格中的 SwiftUI 多个 NavigationLink - 条目保持突出显示

Posted

技术标签:

【中文标题】表单/表格中的 SwiftUI 多个 NavigationLink - 条目保持突出显示【英文标题】:SwiftUI multiple NavigationLinks in Form/Sheet - entry stays highlighted 【发布时间】:2020-09-17 19:33:34 【问题描述】:

我遇到了 Xcode 12 / ios 14 的问题。在带有 NavigationView 的工作表中使用多个 NavigationLink 会导致 NavigationLink 条目在返回页面后保持突出显示。这不仅是模拟器的问题。请参阅随附的 GIF:

有人知道如何解决这个问题吗?

类似问题:SwiftUI - NavigationLink cell in a Form stays highlighted after detail pop(但这不是这里的问题)。

struct ContentView: View 
    
    var body: some View 
        Text("")
            .sheet(isPresented: .constant(true), content: 
                NavigationView 
                    Form 
                        Section 
                            NavigationLink("Link to ViewB", destination: ViewB())
                        
                    
                    .navigationBarTitle("ViewA")
                
            ) 
    


struct ViewB: View 
    @State var selection = 0
    let screenOptions = ["a", "b", "c"]
    var body: some View
        Form 
            Section 
                NavigationLink("Link to ViewC", destination: ViewC())
            
        
        .navigationBarTitle("ViewB")
    


struct ViewC: View 
    var body: some View
        Form 
            Section 
                Text("Test")
            
        
        .navigationBarTitle("ViewC")
    


【问题讨论】:

看起来像 sheet 中的一个错误 - 没有它也可以正常工作。您可以向 Apple 提交错误报告。 终于没有遇到取模的bug (i % 2) == 0 not working :))))))))))))))))) 我笑了河流:) 天哪,是的,我提交了一个错误报告。希望这会很快得到解决。 我最终完全重新实现了表单。 【参考方案1】:

在工作表中使用NavigationLink 时,我也遇到了这个问题。我在 iOS 14 上的解决方案过于调侃didSelectRowAt:UITableView。选择该行时,我取消选择它。有更多代码用于检测它是否在工作表中等,但这是基本的,让它工作代码:

extension UITableView 
    
    @objc static func swizzleTableView() 
        
        guard self == UITableView.self else 
            return
        
        
        let originalTableViewDelegateSelector = #selector(setter: self.delegate)
        let swizzledTableViewDelegateSelector = #selector(self.nsh_set(delegate:))
        
        let originalTableViewMethod = class_getInstanceMethod(self, originalTableViewDelegateSelector)
        let swizzledTableViewMethod = class_getInstanceMethod(self, swizzledTableViewDelegateSelector)
        
        method_exchangeImplementations(originalTableViewMethod!,
                                       swizzledTableViewMethod!)
    
    
    @objc open func nsh_set(delegate: UITableViewDelegate?) 
        nsh_set(delegate: delegate)
        
        guard let delegate =  delegate else  return 
        
        let originalDidSelectSelector = #selector(delegate.tableView(_:didSelectRowAt:))
        let swizzleDidSelectSelector = #selector(self.tableView(_:didSelectRowAt:))
        
        let swizzleMethod = class_getInstanceMethod(UITableView.self, swizzleDidSelectSelector)
        let didAddMethod = class_addMethod(type(of: delegate), swizzleDidSelectSelector, method_getImplementation(swizzleMethod!), method_getTypeEncoding(swizzleMethod!))
        
        if didAddMethod 
            let didSelectOriginalMethod = class_getInstanceMethod(type(of: delegate), NSSelectorFromString("tableView:didSelectRowAt:"))
            let didSelectSwizzledMethod = class_getInstanceMethod(type(of: delegate), originalDidSelectSelector)
            if didSelectOriginalMethod != nil && didSelectSwizzledMethod != nil 
                method_exchangeImplementations(didSelectOriginalMethod!, didSelectSwizzledMethod!)
            
        
    
    
    @objc open func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) 
        self.tableView(tableView, didSelectRowAt: indexPath)

        // This is specifically to fix a bug in SwiftUI, where a NavigationLink is
        // not de-selecting itself inside a sheet.
        tableView.deselectRow(at: indexPath,
                              animated: true)
        
    

(原始 swizzle 代码来自 https://***.com/a/59262109/127853),此代码示例只是添加了 deselectRow 调用。)

别忘了给UITableView.swizzleTableView()打电话,比如application:didFinishLaunchingWithOptions:

【讨论】:

这对我来说在 iPad 上崩溃了,在 iPhone 上它工作正常。你有过类似的经历吗? @dehlen 我在 nsh_set 方法中添加了额外的代码来检测委托是否是 SwiftUI 类,如果是,则只替换该方法,因为我在非 swiftUI 代码上遇到问题(而且它是非 SwiftUI 代码不需要)。我正在apps.apple.com/app/appwage/id834352667#?platform=iphone 中积极使用此代码。 首先感谢您回复我并与我们分享您的解决方案。在 iPhone 上,这对我有用。但是在 iPad 上运行它会导致无限循环,最终导致应用程序崩溃。由于我无法在评论中上传屏幕截图,因此这里有一个显示调试器的链接。如您所见,它在运行didSelectRow 的原始实现时崩溃。这是在任何运行 14.2 和 Xcode 12.2.0 的 iPad 上:cln.sh/ryVMcw【参考方案2】:

将以下修饰符添加到您的 NavigationView 以设置导航视图样式并修复此问题:

.navigationViewStyle(StackNavigationViewStyle())

解释:

默认样式是 DefaultNavigationViewStyle(),来自文档:“当前正在设置样式的视图上下文中的默认导航视图样式”。

由于某种原因,这将在 iPhone 上使用 DoubleColumnNavigationViewStyle 而不是 StackNavigationViewStyle,如果您明确设置样式,它会按预期运行。

【讨论】:

不是每个人都想在 iPad 上使用 iPhone 布局。 提到的问题是,根据我的经验,特定于 iPhone,如果您需要在 iPadOS 上支持 StackNavigationViewStyle,您可以有条件地设置此样式。 我明白了。很有趣。

以上是关于表单/表格中的 SwiftUI 多个 NavigationLink - 条目保持突出显示的主要内容,如果未能解决你的问题,请参考以下文章

swiftui表单没有出现在列表中

SwiftUI 将 navigationBarItems 按钮与“消息”中的大 navigationBarTitle 对齐

列表中的 SwiftUI 多个弹出框

如何向 SwiftUI 表单添加更多行/项目?

SwiftUI Picker 多个错误,核心数据,布局

表格中的多个提交按钮