SwiftUI on iOS 16 - Multiple selection in a List does not work - swift

This is on iOS 16. I'm on Xcode 14.0.
I have the following view:
struct ContentView: View {
struct Ocean: Identifiable, Hashable {
let name: String
let id = UUID()
}
private var oceans = [
Ocean(name: "Pacific"),
Ocean(name: "Atlantic"),
Ocean(name: "Indian"),
Ocean(name: "Southern"),
Ocean(name: "Arctic")
]
#State private var multiSelection = Set<UUID>()
var body: some View {
NavigationView {
List(oceans, selection: $multiSelection) {
Text($0.name)
}
.navigationTitle("Oceans")
.toolbar { EditButton() }
}
Text("\(multiSelection.count) selections")
}
}
This code is taken from https://developer.apple.com/documentation/SwiftUI/List.
I am expecting to see that whenever I click on the "Edit" button, I should be able to select a few items, press "Done", then the bottom would still show the number of items I have selected. However, this is not the case:
I tried to use a debugger, and I found out that whenever I click on "Done" after selecting the items, the multiSelection resets itself to be empty. This used to work on Xcode 13. I can't really find anything on Apple's documentation regarding changes to the EditButton or changes to the List struct.
Update
I filed a bug report and Apple got back to me, they said this is expected behaviour. I guess I misinterpreted the use case for this list selection here.

Before iOS 16 the selection only worked when in editing mode. Now it works also when not editing so I believe the problem is now the selection is being cleared when done is being tapped (so it can be used when not editing).
I think we need to send feedback to request 2 selection bindings, one for editing and one for when not-editing.

i think the issue here is that your identifiers for your list are not stable. Basically every time an ocean object is created, it gets a new UUID. You dont want that UUID to change.
Any time your state property changes, the view may or may not get rebuilt, causing your oceans to get regenerated.
Try storing your oceans in like this:
#State var oceans: [Ocean] = [
Ocean(name: "Pacific"),
Ocean(name: "Atlantic"),
Ocean(name: "Indian"),
Ocean(name: "Southern"),
Ocean(name: "Arctic")
]
Alternatively, you can use the ocean's name as its identifier instead of a UUID that is generated each time its created.

Related

Typing in TextField moves cursor to end (macOS)

In macOS Ventura 13.0.1 and Xcode 14.1, I'm experiencing an issue where when I type in the beginning of a TextField, the cursor for some reason moves to the back.
I was able to reproduce it very simply:
struct ContentView: View {
#State private var text = "DEFG"
var body: some View {
NavigationSplitView {
Text("Sidebar")
} detail: {
VStack {
TextField("Text", text: $text)
}
}
}
}
To reproduce:
Open the app, and move the cursor to the start of the TextField. (so the insertion point is before the "D".
Type a letter, like "A", and the insertion point should jump to the end for some reason.
I have determined the cause is the VStack. For some reason, removing it fixes this issue entirely. However, in my real app, I do need the VStack as I have other elements too.
I wonder what's going on here. Is there some major oversight I have made? Or could this be an issue with SwiftUI?
If anyone is having trouble reproducing, I can upload a video.

SwiftUI: Error message when using .onMove on List built with forEach

I have an array containing some data that I want to present in a View. It should be possible to rearrange the data by holding and dragging it to its new position. I have used the .onMove modifier on a List generated using ForEach to iterate over every element in the list. My code works exactly as expected, but throws the following error/warning:
2022-10-12 18:48:52.833139+0200 Playground[1212:207280]
[EventDispatcher] Found no UIEvent for backing event of type: 11;
contextId: 0xFEEA6094
This only happens when using an actual device (iPhone 12 Pro Max on iOS 16.0.2), not when using the simulator (Xcode Version 14.0.1 (14A400), build in simulator of any device that runs iOS 16).
The code (the whole project is way bigger, but this is enough to trigger the error/warning):
struct ContentView: View {
#State var stuffList = ["Beer", "Chips", "Boardgames"]
var body: some View {
NavigationStack {
List {
ForEach(stuffList, id: \.self) { stuff in
Text(stuff)
}
.onMove { stuffList.move(fromOffsets: $0, toOffset: $1) }
}
}
}
}
Edit:
I have now tried different code snippets from online tutorials, e.g. this one (Hacking in Swift) and I still get this message no matter what I try. As my code seems to work as intended, I will continue my project and treat this as "log noise". If someone knows more about this than I do or simply can confirm having the same issue, it would be highly appreciated.

SWIFTUI: Filtering Lists

I am creating a task management app for my school project, and have hit a wall. My app is based off of the Scrumdinger Tutorial from my Apple to give context. I am currently trying to figure out how to filter items in the list based on two boolean values I have set for task completion, and being delted.
My tasks are structured as followed:
enter image description here
As you can see I've add the two different boolean variables that change based on button clicks within the detailView of the task.
The List of tasks is currently organized in this format, and I'm not exactly sure how to filter based on those value's within the List.
enter image description here
Tasks are stored here:
enter image description here
Any Help would be greatly appreciated!
You can try this simple solution and apply your own button logical, as you want
struct Object: Identifiable, Codable {
var id = UUID()
var taskDone: Bool
var text: String
}
#State var array = [Object(taskDone: true, text: "taskDone1"), Object(taskDone: false, text: "taskDone2")]
var body: some View {
List(array.filter { $0.taskDone }){ (item) in
Text("\(item.text)")
.foregroundColor(.red)
}
}

SwiftUI TextField keyboard flashes and breaks typing in other languages

https://youtu.be/ngExUJ7gyb8
Please observe the different behavior between system keyboard and the one using SwiftUI.
The first 5 seconds using spotlight search, you'll notice the typing is complete and the word picker does not flash for every character input.
In the last 5 second, you'll notice using SwiftUI's keyboard, the typing will be break every couple characters, and the word picker flashes for every new character input.
The same behavior can be observed using the simplest code
struct ContentView: View {
#State private var inputText = ""
var body: some View {
VStack(spacing: 10.0) {
TextField("Tap here", text: $inputText)
}
}
}
Is that a bug of SwiftUI? Or did I miss something?

SwiftUI macOS Scroll a List With Arrow Keys While a TextField is Active

I'd like to use a SwiftUI TextField and a SwiftUI List to render a "search box" above a list of items. Something roughly like the search box available in Safari's Help menu item...which provides a search box where you can always enter text while simultaneously browsing through the list of results using the up and down arrow keys.
I've played with onMoveCommand, focusable, and adjustments to the "parent" NSWindow, but haven't found a clear and obvious way for the TextField to constantly accept input while still being able to navigate the underlying List using the up and down arrow keys. The following code allows for either text to be entered in the TextField, or list entries to be navigated through, but not both at the same time...
struct ContentView: View {
#State var text: String = ""
#State var selection: Int? = 1
var body: some View {
VStack {
TextField("Enter text", text: $text)
List(selection: $selection) {
ForEach((1...100), id: \.self) {
Text("\($0)")
}
}
}
}
}
You can wrap a NSTextField in a NSViewRepresentable and use the NSTextFieldDelegate to intercept the move up and down keys. You can take a look into my suggestions demo.
Source of a text field with suggestions