2

NavigationView在 macCatalyst 上遇到了一个奇怪的问题。下面是一个带有侧边栏和详细视图的简单应用程序。选择侧边栏上的项目会显示带有可滚动列表的详细视图。

第一个一切正常NavigationLink,详细视图显示并且可以自由滚动。但是,如果我选择了一个触发指向第二个详细视图的链接的列表项,则滚动开始,然后冻结。该应用程序仍然有效,只有详细视图滚动被锁定。

相同的代码在 iPad 上运行良好,没有任何冻结。如果我为 macOS 构建,详细视图中的 NavigationLink 将不起作用。

是否有任何已知的解决方法?

这就是它的样子,单击 LinkedView 后,短暂滚动然后视图冻结。仍然可以单击后退按钮或侧边栏上的其他项目,但列表视图被阻止。

在此处输入图像描述

这是代码:ContentView.swift

import SwiftUI

struct ContentView: View {

    var names = [NamedItem(name: "One"), NamedItem(name: "Two"), NamedItem(name:"Three")]

    var body: some View {
        NavigationView {
            List() {
                ForEach(names.sorted(by: {$0.name < $1.name})) { item in
                    NavigationLink(destination: DetailListView(item: item)) {
                        Text(item.name)
                    }
                }
            }
            .listStyle(SidebarListStyle())

            Text("Detail view")
        }
    }
}

struct NamedItem: Identifiable {
    let name: String
    let id = UUID()
}

struct DetailListView: View {

    var item: NamedItem

    let sections = (0...4).map({NamedItem(name: "\($0)")})

    var body: some View {
        VStack {
            List {
                Text(item.name)
                NavigationLink(destination: DetailListView(item: NamedItem(name: "LinkedView"))) {
                    listItem("  LinkedView", "Item")
                        .foregroundColor(Color.blue)
                }

                ForEach(sections) { section in
                    sectionDetails(section)
                }
            }
        }
    }

    let info = (0...12).map({NamedItem(name: "\($0)")})

    func sectionDetails(_ section: NamedItem) -> some View {
        Section(header: Text("Section \(section.name)")) {
            Group {
                listItem("ID", "\(section.id)")
            }
            Text("")
            ForEach(info) { ch in
                listItem("Item \(ch.name)", "\(ch.id)")
            }
        }
    }

    func listItem(_ title: String, _ value: String, tooltip: String? = nil) -> some View {
        HStack {
            Text(title)
                .frame(width: 200, alignment: .leading)
            Text(value)
                .padding(.leading, 10)
        }
    }

}

TestListApp.swift

import SwiftUI

@main
struct TestListApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}
4

3 回答 3

2

我在使用 Mac Catalyst 应用程序时遇到了同样的问题。在真实设备(带有 iOS 14.4.2 的 iPhone 7)上没有问题,但是使用 Mac Catalyst(带有 Big Sur 11.2.3 的 MacBook Pro),导航视图中的滚动非常随机,正如您所解释的那样。我发现问题出在 Macbook 的触控板上,并且与滚动指示器有关,因为使用外接鼠标时问题不存在。所以解决这个问题最简单的方法是在导航视图中隐藏垂直滚动指示器。至少它对我有用。下面是来自根视图“ContentView”的一些代码,我是如何做到的。使用大数据丢失滚动指示器是不幸的,但至少滚动有效。

import SwiftUI

struct TestView: View {
    var body: some View {
        NavigationView {
            List {
                NavigationLink(destination: NewView()) {
                    Text("Navigation Link to new view")
                }
            }
            .onAppear {
                UITableView.appearance().showsVerticalScrollIndicator = false
            }   
        }
    }
}
于 2021-04-08T18:13:26.620 回答
0

我继续尝试NavigationStack并进行了一些修改,使其可以List直接换入和换出行。这避免了我在 NavigationBar 背景中看到的问题。导航栏设置在 上方的级别,NavigationStackView并且对标题的更改通过PreferenceKey. 如果堆栈为空,导航栏上的后退按钮将隐藏。

以下代码使用了swiftui-navigation-stack的PR#44

import SwiftUI

struct ContentView: View {

    var names = [NamedItem(name: "One"), NamedItem(name: "Two"), NamedItem(name:"Three")]

    @State private var isSelected: UUID? = nil

    var body: some View {
        NavigationView {
            List {
                ForEach(names.sorted(by: {$0.name < $1.name})) { item in
                    NavigationLink(destination: DetailStackView(item: item)) {
                        Text(item.name)
                    }
                }
            }
            .listStyle(SidebarListStyle())

            Text("Detail view")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .toolbar { Spacer() }
        }
    }
}

struct NamedItem: Identifiable {
    let name: String
    let depth: Int
    let id = UUID()
    init(name:String, depth: Int = 0) {
        self.name = name
        self.depth = depth
    }
    var linked: NamedItem {
        return NamedItem(name: "Linked \(depth+1)", depth:depth+1)
    }
}

// Preference Key to send title back down to DetailStackView
struct ListTitleKey: PreferenceKey {
    static var defaultValue: String = ""

    static func reduce(value: inout String, nextValue: () -> String) {
        value = nextValue()
    }
}

extension View {
    func listTitle(_ title: String) -> some View {
        self.preference(key: ListTitleKey.self, value: title)
    }
}

// Embed the list view in a NavigationStackView
struct DetailStackView: View {
    var item: NamedItem

    @ObservedObject var navigationStack = NavigationStack()

    @State var toolbarTitle: String = ""

    var body: some View {
        List {
            NavigationStackView(noGroup: true, navigationStack: navigationStack) {
                DetailListView(item: item, linked: item.linked)
                    .listTitle(item.name)
            }
        }
        .listStyle(PlainListStyle())
        .animation(nil)

        // Updated title
        .onPreferenceChange(ListTitleKey.self) { value in
            toolbarTitle = value
        }
        .navigationBarTitleDisplayMode(.inline)
        .navigationTitle("\(toolbarTitle) \(self.navigationStack.depth)")
        .toolbar(content: {
            ToolbarItem(id: "BackB", placement: .navigationBarLeading, showsByDefault: self.navigationStack.depth > 0) {
                Button(action: {
                    self.navigationStack.pop()
                }, label: {
                    Image(systemName: "chevron.left")
                })
                .opacity(self.navigationStack.depth > 0  ? 1.0 : 0.0)
            }
        })
    }
}

struct DetailListView: View {

    var item: NamedItem
    var linked: NamedItem

    let sections = (0...10).map({NamedItem(name: "\($0)")})

    // Use a Navigation Stack instead of a NavigationLink
    @State private var isSelected: UUID? = nil
    @EnvironmentObject private var navigationStack: NavigationStack

    var body: some View {
        Text(item.name)
        PushView(destination: linkedDetailView,
                 tag: linked.id, selection: $isSelected) {
            listLinkedItem("  LinkedView", "Item")
        }

        ForEach(sections) { section in
            if section.name != "0" {
                sectionDetails(section)
            }
        }
    }

    // Ensure that the linked view has a toolbar button to return to this view
    var linkedDetailView: some View {
        DetailListView(item: linked, linked: linked.linked)
            .listTitle(linked.name)
    }

    let info = (0...12).map({NamedItem(name: "\($0)")})

    func sectionDetails(_ section: NamedItem) -> some View {
        Section(header: Text("Section \(section.name)")) {
            Group {
                listItem("ID", "\(section.id)")
            }
            Text("")
            ForEach(info) { ch in
                listItem("Item \(ch.name)", "\(ch.id)")
            }
        }
    }

    func buttonAction() {
        self.isSelected = linked.id
    }

    // Use a button to select the linked view with a single click
    func listLinkedItem(_ title: String, _ value: String, tooltip: String? = nil) -> some View {
        HStack {
            Button(title, action: buttonAction)
                .foregroundColor(Color.blue)
            Text(value)
                .padding(.leading, 10)
        }
    }

    func listItem(_ title: String, _ value: String, tooltip: String? = nil) -> some View {
        HStack {
            Text(title)
                .frame(width: 200, alignment: .leading)
            Text(value)
                .padding(.leading, 10)
        }
    }
}
于 2021-02-12T08:51:18.960 回答
0

好的,所以我设法找到了一种解决方法,所以我想我会发布这个寻求帮助,直到似乎是 macCatalyst SwiftUI 错误被修复。我已经发布了列表冻结问题的雷达:FB8994665

解决方法是NavigationLink只使用可以导航的一系列页面的第一级(它给了我侧边栏和工具栏),然后使用NavigationStack 包来管理到其他页面的链接。

我遇到了其他几个问题。

首先,NavigationView当滚动链表视图时,工具栏会失去其背景(除非窗口散焦并重新聚焦),这似乎是另一个催化剂 SwiftUI 错误。我通过设置工具栏背景颜色解决了这个问题。

第二个问题是,在 macCatalyst 下onTouch,NavigationStack 的 PushView 标签中使用的视图修饰符不适用于大多数单击。它只会在双击时持续触发。我通过使用按钮替换标签来解决这个问题。

这是代码,不再有列表冻结!

import SwiftUI
import NavigationStack

struct ContentView: View {

    var names = [NamedItem(name: "One"), NamedItem(name: "Two"), NamedItem(name:"Three")]

    @State private var isSelected: UUID? = nil

    init() {
        // Ensure toolbar is allways opaque
        UINavigationBar.appearance().backgroundColor = UIColor.secondarySystemBackground
    }

    var body: some View {
        NavigationView {
            List {
                ForEach(names.sorted(by: {$0.name < $1.name})) { item in
                    NavigationLink(destination: DetailStackView(item: item)) {
                        Text(item.name)
                    }
                }
            }
            .listStyle(SidebarListStyle())

            Text("Detail view")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
                .toolbar { Spacer() }
        }
    }
}

struct NamedItem: Identifiable {
    let name: String
    let id = UUID()
}

// Embed the list view in a NavigationStackView
struct DetailStackView: View {
    var item: NamedItem

    var body: some View {
        NavigationStackView {
            DetailListView(item: item)
        }
    }
}

struct DetailListView: View {

    var item: NamedItem

    let sections = (0...10).map({NamedItem(name: "\($0)")})

    var linked = NamedItem(name: "LinkedView")

    // Use a Navigation Stack instead of a NavigationLink
    @State private var isSelected: UUID? = nil
    @EnvironmentObject private var navigationStack: NavigationStack

    var body: some View {
        List {
            Text(item.name)
            PushView(destination: linkedDetailView,
                     tag: linked.id, selection: $isSelected) {
                listLinkedItem("  LinkedView", "Item")
            }

            ForEach(sections) { section in
                if section.name != "0" {
                    sectionDetails(section)
                }
            }
        }
        .navigationBarTitleDisplayMode(.inline)
        .navigationTitle(item.name)
    }

    // Ensure that the linked view has a toolbar button to return to this view
    var linkedDetailView: some View {
        DetailListView(item: linked)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: {
                        self.navigationStack.pop()
                    }, label: {
                        Image(systemName: "chevron.left")
                    })
                }
            }
    }

    let info = (0...12).map({NamedItem(name: "\($0)")})

    func sectionDetails(_ section: NamedItem) -> some View {
        Section(header: Text("Section \(section.name)")) {
            Group {
                listItem("ID", "\(section.id)")
            }
            Text("")
            ForEach(info) { ch in
                listItem("Item \(ch.name)", "\(ch.id)")
            }
        }
    }

    // Use a button to select the linked view with a single click
    func listLinkedItem(_ title: String, _ value: String, tooltip: String? = nil) -> some View {
        HStack {
            Button(title, action: {
                self.isSelected = linked.id
            })
            .foregroundColor(Color.blue)
            Text(value)
                .padding(.leading, 10)
        }
    }

    func listItem(_ title: String, _ value: String, tooltip: String? = nil) -> some View {
        HStack {
            Text(title)
                .frame(width: 200, alignment: .leading)
            Text(value)
                .padding(.leading, 10)
        }
    }

}
于 2021-02-08T14:06:58.820 回答