boldi24
boldi24

Reputation: 143

Selected list row's background remains grey (selected) after navigating back in List (SwiftUI). iOS14 + Xcode12

The selected row remains grey after navigating back from the detail view. Happening both on simulator and real device, only on iOS 14. Does anyone know how to remove it so it behaves the same as on iOS 13 (doesn't remain selected)? This is the only code in the project. (No other navigation's or anything).

let items = ["item1", "item2"]

struct ContentView: View {
    var body: some View {
        NavigationView {
            VStack {
                Text("Hello")
                
                List(items, id: \.self) { item in
                    NavigationLink(destination: Text(item)) {
                        Text(item)
                    }
                }
                .listStyle(PlainListStyle())
            }
        }
//        .navigationViewStyle(StackNavigationViewStyle()) // didn't solve the problem
    }
}

This is how it looks

Upvotes: 14

Views: 2679

Answers (6)

Beader Chen
Beader Chen

Reputation: 186

This is the trick. Just create a ZStack and put an empty button above.

var body: some View {
    List {
        ForEach(data, id: \.self) { item in
            ZStack {
                Button("") {}
                NavigationLink(destination: ItemView(item: item)) {
                    ItemRow(item: item)
                }
            }
        }
    }
}

Upvotes: 17

lorenzo
lorenzo

Reputation: 1743

Here is what worked for me:

@State var selection: Int? = nil

var body: some View {
    List {
        ForEach(self.model.data, id: \.self) { item in
            ZStack {
                
                Button("") {
                    self.selection = item.id
                }
                NavigationLink(destination: ItemView(item: item), tag: item.id, selection: self.$selection) {
                    Spacer()
                }.hidden()
                ItemRow(item: item)

            }
            
        }
    }
}

Upvotes: 0

papa
papa

Reputation: 3

You can wrap your view like this, it is useful especially if you are using list etc. This solution also gets rid of the caret.

ZStack {
     NavigationLink(destination: DestinationView()) {
         EmptyView()
     }

     Rectangle().background(Color(UIColor.systemBackground)).foregroundColor(Color(UIColor.systemBackground))
                    
     ViewToClick()
}

Upvotes: 0

Tao-Nhan Nguyen
Tao-Nhan Nguyen

Reputation: 5926

Here is the way I made it work. I have found some very rare cases where there is a bit of a lag for the highlight to disappear, but so far it helped a lot. This example uses the MVVM pattern, hopefully you get the idea.

You can access the underlying UITableView behind the SwiftUI List with this third party library :

https://github.com/siteline/SwiftUI-Introspect

and then you can store the tableView instance, and flush the selection when the table appears and disappears (somehow it worked better to do it in both, as those blocks may execute with a bit of lag depending on how aggressively the user navigates)

List {
// ...
}
.introspectTableView { tableView in
                         self.viewModel.bindToTableView(tableView)
                      }
.onAppear(perform: {
      self.viewModel.clearTableViewSelection()
})
.onDisappear(perform: {
      self.viewModel.clearTableViewSelection()
})

And in the viewModel, here are the functions

func bindToTableView(_ tableView: UITableView) {
    self.tableView = tableView
}

func clearTableViewSelection() {
    // This is a iOS14 hack that prevents clicked cell background view to remain highlighted when we come back to the screen
    if #available(iOS 14, *){
        DispatchQueue.main.async {
            if let selectedIndexPath = self.tableView?.indexPathForSelectedRow {
                self.tableView?.deselectRow(at: selectedIndexPath, animated: false)
                if let selectedCell = self.tableView?.cellForRow(at: selectedIndexPath) {
                    selectedCell.setSelected(false, animated: false)
                }
                
            }
        }
    }
}

Upvotes: 1

Cameron
Cameron

Reputation: 712

I had the same problem as soon as I used StackNavigationViewStyle(). The issue resolved itself for me by using DefaultNavigationViewStyle().

Upvotes: 2

phil Schn
phil Schn

Reputation: 17

I actually found a workaround with lazystack and then putting the row/cell in an Hstack together with some padding and a divider. But the performance is way worse than with a list.

Edit If you put the list outside of another View (in your example dont embed it in a VStack) it will work fine. So you have to put your list in a separate View-Struct and embed this one in your ContentView. Something like this:

    struct ExtractedListView: View {
     var listItems:[something]
        List(listItems, id: \.self) { item in
           NavigationLink(destination: Text(item)) {
             Text(item)
          }
    }


struct ContentView : View {
   NavigationView {
    VStack {
       Text("Hello")
       ExtractedListView(listItems: items)
    }
  }
}

Upvotes: 0

Related Questions