简体   繁体   English

SwiftUI - 表单中的 NavigationLink 单元格在详细信息弹出后保持突出显示

[英]SwiftUI - NavigationLink cell in a Form stays highlighted after detail pop

In iOS 14, it appears that NavigationLink s do not become deselected after returning in a Form context.在 iOS 14 中,看起来NavigationLink在返回Form上下文后不会被取消选择。 This is also true for Form Picker s and anything else that causes the presentation of another View from a list (giving a highlight context to the presenting cell).这也适用于Form Picker和其他任何导致从列表中显示另一个View的东西(为呈现单元格提供突出显示的上下文)。

I didn't notice this behaviour in iOS 13.我没有注意到 iOS 13 中的这种行为。

Is there a way to 'deselect' the highlighted row once the other view is dismissed?一旦另一个视图被关闭,有没有办法“取消选择”突出显示的行?

Example code:示例代码:

struct ContentView: View {

    var body: some View {
        Form {
            NavigationLink(destination: Text("Detail")) {
                Text("Link")
            } 
        }
    }

}

(Different) Example visual: (不同)视觉示例:

例子

In my case this behaviour appeared when using any Viewcontent (eg Text(), Image(), ...) between my NavigationView and List/Form .在我的情况下,这种行为出现在我的NavigationViewList/Form之间使用任何 Viewcontent(例如 Text()、Image()、...)时。

var body: some View {
    
    NavigationView {
        VStack {
            Text("This text DOES make problems.")
            List {
                NavigationLink(destination: Text("Doesn't work correct")) {
                    Text("Doesn't work correct")
                }
            }
        }
    }
}

Putting the Text() beneath the List does not make any problems:将 Text()放在List之下不会产生任何问题:

var body: some View {
    
    NavigationView {
        VStack {
            List {
                NavigationLink(destination: Text("Does work correct")) {
                    Text("Does work correct")
                }
            }
            Text("This text doesn't make problems.")
        }
    }
}

This is definitely a XCode 12 bug.这绝对是一个 XCode 12 错误。 As more people report this, as earlier it gets resolved.随着越来越多的人报告这个问题,它会越早得到解决。

I have also run into this issue and believed I found the root cause in my case.我也遇到了这个问题,并相信我找到了我的案例的根本原因。

In my case I had.a structure like the following:在我的情况下,我有一个如下结构:

struct Page1View: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink("Page 2", destination: Page2View())
            }
                .listStyle(GroupedListStyle())
                .navigationBarTitle("Page 1")
        }
    }
}

struct Page2View: View {
    var body: some View {
        List {
            NavigationLink("Page 3", destination: Text("Page 3"))
        }
            .listStyle(GroupedListStyle())
            .navigationBarTitle("Page 2")
    }
}

This issue would occur on the NavigationLink to Page 3. In the console output this error was showing when that link was used:此问题会发生在 NavigationLink 到页面 3 上。在控制台输出中,使用该链接时会显示此错误:

2021-02-13 16:41:00.599844+0000 App[59157:254215] [Assert] displayModeButtonItem is internally managed and not exposed for DoubleColumn style. Returning an empty, disconnected UIBarButtonItem to fulfill the non-null contract.

I discovered that I needed to apply .navigationViewStyle(StackNavigationViewStyle()) to the NavigationView and this solved the problem.我发现我需要将.navigationViewStyle(StackNavigationViewStyle())应用于 NavigationView,这解决了问题。

Ie IE

struct Page1View: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink("Page 2", destination: Page2View())
            }
                .listStyle(GroupedListStyle())
                .navigationBarTitle("Page 1")
        }
            .navigationViewStyle(StackNavigationViewStyle())
    }
}

Been fighting this issue half day today and came to this post that helped me to understand that issue appears if Text , Button or something else placed between NavigationView and in my case List .今天一直在与这个问题作斗争半天,来到这篇文章,帮助我理解如果TextButton或其他东西放在NavigationView和我的案例List之间会出现这个问题。 And I found solution that worked for me.我找到了对我有用的解决方案。 Just add .zIndex() for the item.只需为该项目添加.zIndex() .zIndex() must be higher than for List Tried with Xcode 12.5 . .zIndex()必须高于List Tried with Xcode 12.5

var body: some View {
    NavigationView {
        VStack {
            Text("This text DOES make problems.")
                .zIndex(1.0)
            List {
                NavigationLink(destination: Text("Doesn't work correct")) {
                    Text("Doesn't work correct")
                }
            }
        }
    }
}

I did a bit more tinkering, it turns out this was caused due by having the UIHostingController being nested in a UINavigationController and using that navigation controller.我做了一些修补,结果证明这是由于UIHostingController嵌套在UINavigationController并使用该导航控制器造成的。 Changing the navigation stack to use a SwiftUI NavigationView instead resolved this issue.更改导航堆栈以使用 SwiftUI NavigationView解决了此问题。

Similar to what @pawello2222 says in the question comments, I think the underlying cause is something to do with SwiftUI not understanding the proper navigation hierarchy when the external UINavigationController is used.与@pawello2222 在问题评论中所说的类似,我认为根本原因与 SwiftUI 在使用外部UINavigationController时不理解正确的导航层次结构有关。

This is just one instance where this is fixed though, I'm still experiencing the issue in various other contexts depending on how my view is structured.这只是修复了此问题的一个实例,根据我的视图结构,我仍在各种其他上下文中遇到此问题。

I've submitted an issue report FB8705430 to Apple, so hopefully this is fixed sometime soon.我已经向 Apple 提交了一份问题报告FB8705430 ,所以希望这个问题很快得到解决。

Before (broken):之前(损坏):

struct ContentView: View {
    var body: some View {
        Form {
             NavigationLink(destination: Text("test")) {
                 Text("test")
             }
        }
    }
}

// (UIKit presentation context)
let view = ContentView() 
let host = UIHostingController(rootView: view)
let nav = UINavigationController(rootViewController: host)
present(nav, animated: true, completion: nil)

After (working):之后(工作):

struct ContentView: View {
    var body: some View {
        NavigationView {
            Form {
                NavigationLink(destination: Text("test")) {
                    Text("test")
                }
            }
        }
    }
}

// (UIKit presentation context)
let view = ContentView()
let host = UIHostingController(rootView: view)
present(host, animated: true, completion: nil)

This is definitely a bug in List , for now, my work-around is refreshing the List by changing the id, like this:这绝对是List一个错误,目前,我的解决方法是通过更改 id 来刷新List ,如下所示:

struct YourView: View {
    @State private var selectedItem: String?
    @State private var listViewId = UUID()

    var body: some View {
            List(items, id: \.id) {
                NavigationLink(destination: Text($0.id), 
                               tag: $0.id, 
                               selection: $selectedItem) {
                  Text("Row \($0.id)")
                }
            }
            .id(listViewId)
            .onAppear {
                if selectedItem != nil {
                    selectedItem = nil
                    listViewId = UUID()
                }
            }
    } 
}

I made a modifier based on this that you can use:我基于此制作了一个修饰符,您可以使用:

struct RefreshOnAppearModifier<Tag: Hashable>: ViewModifier {
    @State private var viewId = UUID()
    @Binding var selection: Tag?
    
    func body(content: Content) -> some View {
        content
            .id(viewId)
            .onAppear {
                if selection != nil {
                    viewId = UUID()
                    selection = nil
                }
            }
    }
}

extension View {
    func refreshOnAppear<Tag: Hashable>(selection: Binding<Tag?>? = nil) -> some View {
        modifier(RefreshOnAppearModifier(selection: selection ?? .constant(nil)))
    }
}

use it like this:像这样使用它:

List { ... }
  .refreshOnAppear(selection: $selectedItem)

I managed to solve it by adding ids to the different components of the list, using binding and resetting the binding on .onDisappear我设法通过向列表的不同组件添加 id,使用绑定并重置 .onDisappear 上的绑定来解决它

struct ContentView: View {
    @State var selection: String? = nil

    var body: some View {
        NavigationView {
            VStack {
                Text("Hello, world!")
                    .padding()
                List {
                    Section {
                        NavigationLink( destination: Text("Subscreen1"), tag: "link1", selection: $selection ) {
                            Text("Subscreen1")
                        }.onDisappear {
                            self.selection = nil
                        }
                        NavigationLink( destination: Text("Subscreen2"), tag: "link2", selection: $selection ) {
                            Text("Subscreen2")
                        }.onDisappear {
                            self.selection = nil
                        }
                    }.id("idSection1")
                }
                .id("idList")
            }
        }
    }
}

I've also run into this issue and it seemed related to sheets as mentioned here .我也遇到过这个问题,它似乎与这里提到的工作表有关。

My solution was to swizzle UITableView catch selections, and deselect the cell.我的解决方案是调整UITableView捕捉选择,并取消选择单元格。 The code for doing so is here .这样做的代码在这里 Hopefully this will be fixed in future iOS.希望这将在未来的 iOS 中得到修复。

This is the workaround I've been using until this List issue gets fixed.这是我一直在使用的解决方法,直到此 List 问题得到修复。 Using the Introspect library, I save the List's UITableView.reloadData method and call it when it appears again.使用Introspect库,我保存 List 的 UITableView.reloadData 方法并在它再次出现时调用它。

import SwiftUI
import Introspect

struct MyView: View {

    @State var reload: (() -> Void)? = nil

    var body: some View {
        NavigationView {
            List {
                NavigationLink("Next", destination: Text("Hello"))
            }.introspectTableView { tv in
                self.reload = tv.reloadData
            }.onAppear {
                self.reload?()
            }
        }
    }
}

Adding .navigationViewStyle(StackNavigationViewStyle()) to NavigationView fixed it for me..navigationViewStyle(StackNavigationViewStyle())添加到NavigationView为我修复了它。

Suggested in this thread: https://developer.apple.com/forums/thread/660468在此线程中建议: https : //developer.apple.com/forums/thread/660468

This is my solution to this issue.这是我对这个问题的解决方案。

// This in a stack in front of list, disables large navigation title from collapsing by disallowing list from scrolling on top of navigation title
public struct PreventCollapseView: View { 

    @State public var viewColor: Color?

    public init(color: Color? = nil) {
        self.viewColor = color
    }
    
    public var body: some View {
        Rectangle()
            .fill(viewColor ?? Color(UIColor(white: 0.0, alpha: 0.0005)))
            .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: 1)
    }
}

// handy modifier..
extension List {
    
    public func uncollapsing(_ viewUpdater: Bool) -> some View {
        
        VStack(spacing: 0.0) {
            Group {
                PreventCollapseView()
                self
            }.id(viewUpdater)
        }
    }
}

struct TestView: View {

    @State var updater: Bool = false
    
    var body: some View {

        List {
            Text("Item one")
            Text("Item two")
            Text("Manually refresh")
                .onTapGesture { DispatchQueue.main.async { updater.toggle() } }
                .onAppear { print("List was refreshed") }
        }
        .uncollapsing(updater)
        .clipped()
        .onAppear { DispatchQueue.main.async { updater.toggle() }} // Manually refreshes list always when re-appearing/appearing

    }
}

Add a NavigationView, configure for largeTitle, and embed TestView and it's all set.添加 NavigationView,为 largeTitle 配置,并嵌入 TestView,一切就绪。 Toggle updater to refresh.切换更新程序以刷新。

Having the same Problem.有同样的问题。 The weird thing is, that the exact same code worked in iOS13.奇怪的是,完全相同的代码在 iOS13 中工作。 I'm having this issue with a simple list:我在一个简单的列表中遇到了这个问题:

struct TestList: View {
    let someArray = ["one", "two", "three", "four", "five"] 
    var body: some View {
        List(someArray, id: \.self) { item in 
                NavigationLink(
                    destination: Text(item)) {
                    Text(item)
                }.buttonStyle(PlainButtonStyle()) 
        }.navigationBarTitle("testlist") 
    }
}

This is embedded in:这嵌入在:

struct ListControllerView: View {
    @State private var listPicker = 0
    var body: some View {
        NavigationView{
            Group{
                VStack{
                    Picker(selection: $listPicker, label: Text("Detailoverview")) {
                        Text("foo").tag(0)
                        Text("bar").tag(1)
                        Text("TestList").tag(2)
                    }

This is inside a Tabbar.这是在 Tabbar 内。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM