Hello dear community!
I'm still new to SwiftUI and going through the official Introducing SwiftUI Tutorials (basically, building the Landmarks app)
And I'm struggle with some behavior I noticed in the macOS version of the Landmarks app.
So, here is the idea, there is a list of Landmarks (the simplified version from the tutorial):
struct LandmarkList: View {
@Environment(ModelData.self) var modelData
@State private var selectedLandmark: Landmark?
var index: Int? {
modelData.landmarks.firstIndex(where: { $0.id == selectedLandmark?.id })
}
var body: some View {
@Bindable var modelData = modelData
NavigationSplitView {
List(selection: $selectedLandmark) {
ForEach(modelData.landmarks) { landmark in
NavigationLink {
LandmarkDetail(landmark: landmark)
} label: {
LandmarkRow(landmark: landmark)
}
.tag(landmark)
}
}
.navigationTitle("Landmarks")
.frame(minWidth: 300)
} detail: {
Text("Select a landmark")
}
.focusedValue(\.selectedLandmark, $modelData.landmarks[index ?? 0])
}
}
And there are a few helper structs which makes the possibility of the Marking a selected landmark as favorite (or remove) via shortcut and via menu:
struct LandmarkCommands: Commands {
@FocusedBinding(\.selectedLandmark) var selectedLandmark
var body: some Commands {
SidebarCommands()
CommandMenu("Landmark") {
Button("\(selectedLandmark?.isFavorite == true ? "Remove" : "Mark") as Favorite") {
selectedLandmark?.isFavorite.toggle()
}
.keyboardShortcut("f", modifiers: [.shift, .option])
.disabled(selectedLandmark == nil)
}
}
}
private struct SelectedLandmarkKey: FocusedValueKey {
typealias Value = Binding<Landmark>
}
extension FocusedValues {
var selectedLandmark: Binding<Landmark>? {
get {
self[SelectedLandmarkKey.self]
} set {
self[SelectedLandmarkKey.self] = newValue
}
}
}
So, with this setup which is presented in the tutorial I notice 3 issues:
On the first launch of the app, if I try to select a landmark — it's get unselected instantly. It I try to select it again — it works
Marking a selected Landmark as favorite via shortcut (f+shift+opt) or via menu makes the selected Landmark unselected
On the Landmark details — marking a Landmark as Favorite also makes the landmark unselected.
You can check it on your own if you download the completed project from this page: https://developer.apple.com/tutorials/swiftui/creating-a-macos-app
But could someone please explain why it's happening? And how to avoid such a bad UX with unselecting items in the list?
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
I wonder what is the right way to combine List and Grid with GridRows together?
In Apple's official tutorial on Model data with custom types there is the following example of Grid:
struct ContentView: View {
@State private var players: [Player] = [
Player(name: "Elisha", score: 0),
Player(name: "Andre", score: 0),
Player(name: "Jasmine", score: 0),
]
var body: some View {
VStack(alignment: .leading) {
Text("Score Keeper")
.font(.title)
.bold()
.padding(.bottom)
Grid {
GridRow {
Text("Player")
.gridColumnAlignment(.leading)
Text("Score")
}
.font(.headline)
ForEach($players) { $player in
GridRow {
TextField("Name", text: $player.name)
Text("\(player.score)")
Stepper("\(player.score)", value: $player.score)
.labelsHidden()
}
}
}
.padding(.vertical)
Button("Add Player", systemImage: "plus") {
players.append(Player(name: "", score: 0))
}
Spacer()
}
.padding()
}
}
And there is a practicing exercise which asks
Use a List view and an EditButton so people can reorder the list of players.
No matter how I tried to integrate the List with EditButton in this view with Grid, I always failed.
If I embed the Grid inside of the List I get the only one item in the list which is not editable
If I use List inside of the Grid, the whole idea behind the usage of Grid doesn't make sense anymore since the alignment of the GridRows follows the List.
And in general, it feels like combining Grid and List is not a good idea and they should be used separately.
What do you think?
Topic:
UI Frameworks
SubTopic:
SwiftUI
Hello dear community!
I'm a new to SwiftUI and going through the official Develop in Swift Tutorials.
I've just finished the chapter Customize views with properties where it was show that it's possible to declare computed properties like this:
var iconName: String {
if isRainy {
return "cloud.rain.fill"
} else {
return "sun.max.fill"
}
}
And then use it as follows:
var body: some View {
VStack {
Text(day)
Image(systemName: iconName)
}
}
This is really cool and I liked it and I decided that I want to do the similar thing for the project from the previous chapter (ChatBubble), so I decided to declare the following property in my custom ChatBubble view:
struct ChatBubble: View {
let text: String
let color: Color
let isEllipsis: Bool
var shape: Shape {
if isEllipsis {
Ellipse()
} else {
RoundedRectangle(cornerRadius: 8)
}
}
var body: some View {
Text(text)
.padding()
.background(color, in: shape)
}
}
But first, I got a warning where I declare shape: Shape that it must be written any Shape like this:
var shape: any Shape { …
But in both cases I got a error in the body:
'buildExpression' is unavailable: this expression does not conform to 'View'
What does it mean? Why I can't use the computed property with shape like the computed property for String?
Thank you very much in advance.
Topic:
UI Frameworks
SubTopic:
SwiftUI