0

Problem

This only occurs when building a target for MacCatalyst. It works fine on iPhones (real devices and simulators).

I make a parent list inside of a NavigationView, call it “Query View”.

Each item in the QueryView is a NavigationLink. One of these NavigationLink’s destination view is a view containing another list (Model View). When navigating to the ModelView, its event list typically freezes up after an initial scroll or two. After it freezes, the only way to unfreeze it is to either pop out of the view and push back in, resize the window, or sometimes add/remove something from the list.

Minimal Reproducible Example

The steps below are for a minimal reproducible example:

  1. New Xcode project using the UIKit LifeCycle and SwiftUI Interface
  2. Tick the MacCatalyst option
  3. Add scene delegate code
  4. Add all other code into one file

Notes

• Sometimes its scrolls fine for a little bit, but eventually it always freezes.

• The CPU is 0% after freeze but then goes to a typical %15 percent when trying to scroll while frozen.

• The list is the only piece of UI that freezes, everything else is functional.

Scene Delegate Code

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let rootVM = RootView.ViewModel()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UIHostingController(rootView: RootView(vm: rootVM))
            self.window = window
            window.makeKeyAndVisible()
        }
    }

All Other Code


import SwiftUI

struct RootView: View {
    
    @ObservedObject var vm:ViewModel
    
    var body: some View {
        NavigationView {
            List {
                ForEach(vm.roles, id: \.rawValue) { role in
                    NavigationLink(
                        role.rawValue,
                        destination: viewForRole(role: role)
                    )
                }
            }
        }
        .navigationBarTitle("Roles")
    }
    
    @ViewBuilder func viewForRole(role: ViewModel.Role) -> some View {
        if role == .admin {
            QueryView(vm: vm.queryVM)
        }else if role == .moderator {
            Text("Coming soon!")
        }else{
            Text(role.rawValue)
        }
    }
}

extension RootView {
    
    class ViewModel:ObservableObject {
        
        let queryVM = QueryView.ViewModel()
        
        @Published var roles:[Role]
        
        enum Role:String, CaseIterable {
            case admin = "Admin"
            case moderator = "Moderator"
        }
        
        init() {
            self.roles = Role.allCases
        }
    }
}



struct QueryView:View {
    
    @ObservedObject var vm:ViewModel
    
    var body: some View {
        VStack(alignment: .center) {
            TextField("Search", text: $vm.searchValue)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .frame(maxWidth: 250)
                .padding()
            List {
                ForEach(vm.models, id: \.self) { model in
                    NavigationLink(model, destination: ModelView(vm: ModelView.ViewModel()))
                }
            }
            Button("Add Model") {
                vm.addModel()
            }
            .padding()
        }
        .navigationBarTitle("Query View")
    }
    
}

extension QueryView {
    
    class ViewModel:ObservableObject {
        
        @Published var models:[String]
        
        @Published var searchValue:String
        
        init() {
            self.searchValue = "Just for looks..."
            self.models = [String]()
            for i in 0..<20 {
                models.append("Model \(i+1)")
            }
        }
        
        
        func addModel() {
            self.models.append("Model \(models.count + 1)")
        }
    }
}

struct ModelView:View {
    
    @ObservedObject var vm:ViewModel
    
    var body: some View {
        HStack {
            List {
                ForEach(vm.events, id: \.self) { event in
                    Text(event)
                }
            }
            .frame(maxWidth: 300)
            Text("Other Model Form")
                .frame(maxWidth: .infinity)
        }
        .navigationBarTitle("Model View")
    }
}


extension ModelView {
    
    class ViewModel:ObservableObject {
        
        var events:[String]
        
        init() {
            self.events = [String]()
            for i in 0..<30 {
                events.append("Event \(i+1)")
            }
        }
    }
}
KissTheCoder
  • 679
  • 6
  • 16
  • Interesting, In the console I get `[Lifecycle] Scene state change error: the scene is no longer connected. (NSMenuBarScene_9607F855-ECA8-4CF0-BB2F-DE9D09E7003B)`. It might need an Apple bug report to try to get an answer. As a workaround I suggest you stick with the SwiftUI App Lifecycle – lorem ipsum Jan 18 '21 at 16:34
  • @loremipsum It looks like the problem still persists for me. Even while using the SwiftUI App Lifecycle, I still get the `Scene state change error: the scene is no longer connected. (NSMenuBarScene_2BB9DB4D-C1E9-469B-83EB-41DADCE249A6)` as well. I'll try filing with Apple, thanks. – KissTheCoder Jan 18 '21 at 16:58
  • I ran into the same problem, and posted a similar question before running into yours. I managed to find a workaround today, see my post https://stackoverflow.com/questions/66087776/list-scroll-freeze-on-catalyst-navigationview/66103224#66103224 – Guy Brooker Feb 08 '21 at 14:08

0 Answers0