Compose custom layouts with SwiftUI

RSS for tag

Discuss the WWDC22 Session Compose custom layouts with SwiftUI

Posts under wwdc2022-10056 tag

7 Posts

Post

Replies

Boosts

Views

Activity

MyEqualWidthHStack WWDC example not working for me
Hi I really liked the video by Paul Lettieri, creating a replacement for HStack that gives equal width to each, so I wrote one for my app. But the app doesn't compile for me. It gives me "Trailing closure passed to parameter of type 'HorizontalAlignment' that does not accept a closure" against the top VStack, not the place where the equal lengths HStack replacement appears. This is my version of his struct: extension LayoutSubviews {       func maxSize() -> CGSize {     let subviewSizes = map { $0.sizeThatFits(.unspecified) }     return subviewSizes.reduce(CGSize.zero) { CGSize(width: Swift.max($0.width, $1.width), height: Swift.max($0.height, $1.height)) }   }// maxSize()       func spacing() -> [Double] {     return indices.map { index in       guard index < count - 1 else { return 0.0 }       return self[index].spacing.distance(to: self[index + 1].spacing, along: .horizontal)     }   }// spacing()     }// extension LayoutSubviews struct EqualWidthHStack: Layout {       func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {     let maxsize = subviews.maxSize()     let totalSpacing = subviews.spacing().reduce(0) { $0 + $1 }     return CGSize(width: maxsize.width * Double(subviews.count) + totalSpacing, height: maxsize.height)   }// sizeThatFits       func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {     let maxsize = subviews.maxSize()     let spacing = subviews.spacing()     let sizeProposal = ProposedViewSize(width: maxsize.width, height: maxsize.height)     var x = bounds.minX + maxsize.width / 2     for index in subviews.indices {       subviews[index].place(at: CGPoint(x: x, y: bounds.midX), anchor: .center, proposal: sizeProposal)       x += maxsize.width + spacing[index]     }   }// placeSubviews     }// EqualWidthHStack I wrote this trivial View to test it: struct ContentView: View {   var body: some View {     VStack {       HStack {         Button("Hi") { print("Hi!") }         Button("Hello") { print("Hello!") }         Button("Good evening") { print("Good evening!") }       }       EqualWidthHStack {         Button("Hi") { print("Hi!") }         Button("Hello") { print("Hello!") }         Button("Good evening") { print("Good evening!") }       }       Image(systemName: "globe")         .imageScale(.large)         .foregroundColor(.accentColor)       Text("Hello, world!")     }   } } I'm using Version 14.0 beta 2 (14A5229c) of Xcode. I was having a problem with the exact same message in another app with a much more complex main view. I hope someone can tell me what to do or what is going on here. Regards, Mark
5
1
2.1k
Feb ’24
Subviews don't apply assigned size in custom EqualSizeZStack layout
I tried to modify CustomLayoutProject in order to create some kind of ZStack which resizes child views to size of the largest one and positions them one over another with some vertical offset. And I'm stuck. Setup is next: I've created EqualSizeZStack: Layout and put in instead of ButtonStack() in ContentView like this: EqualSizeZStack { Buttons() }.border(.blue) I've restricted buttons width by setting their text width .frame(width: 200) instead of .frame(maxWidth: .infinity). Updated Goldfish model text to be 4x longer, so related button can grow up in restricted width (and it does). Layout container gets requested height, and largest size is calculated and assigned correctly (this logic was not changed). But smaller buttons just refuse to apply largest button size no matter what I do. What did I miss? func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { guard !subviews.isEmpty else { return } let maxSize = maxSize(subviews: subviews) let placementProposal = ProposedViewSize(width: maxSize.width, height: maxSize.height) var nextY = bounds.minY for index in subviews.indices { subviews[index].place( at: CGPoint(x: bounds.midX, y: nextY), anchor: .top, proposal: placementProposal) nextY += vOffset } }
0
0
518
Apr ’23
Growing entries to natural width of column in SwiftUI Grid
The new Grid View looks like it brings us a simple way to get row-column layouts into our SwiftUI apps! I wonder if someone can help me with one aspect: I have a situation similar to the one described for the Pet Leaderboard App in the "Compose custom layouts with SwiftUI" video. But I want to save space by putting the Cat/Goldfish/Dog voting buttons in the leaderboard table itself, in the first column, instead of the labels. Here's the code for a simplified version of that table: import SwiftUI struct TableDemo: View { var body: some View { Grid(alignment: .leading) { GridRow { Button { // vote for Cat } label: { Text("Cat").fontWeight(.bold).foregroundColor(.white).padding().background(.green).cornerRadius(8) } Text("23").fontWeight(.bold).foregroundColor(.white).gridColumnAlignment(.trailing) } GridRow { Button { // vote for Goldfish } label: { Text("Goldfish").fontWeight(.bold).foregroundColor(.white).padding().background(.green).cornerRadius(8) } Text("3").fontWeight(.bold).foregroundColor(.white) } } .padding(20) .background(Color(red: 50.0/255, green: 85.0/255, blue: 125.0/255)) .cornerRadius(16) } } struct TableDemo_Previews: PreviewProvider { static var previews: some View { TableDemo() } } which looks like this: That result is visually unappealing because the buttons are inconsistent. It's nice that the Grid column is as wide as the widest button - that part is a big win from using the Grid View! But I'd like to get them to conform to the width of the widest button. The normal solution for some situations is to set an infinite maxWidth to the buttons. eg: Text("Cat").fontWeight(.bold).frame(maxWidth: .infinity).foregroundColor(.white).padding().background(.green).cornerRadius(8) But that results in the entire column expanding to take all available space: So what can we do, other than resorting to a PreferenceKey based approach, to solve this? Is there an official Grid-centric way of fixing this? This is the desired layout (I manually sized the buttons):
0
0
784
Oct ’22
MyEqualWidthHStack WWDC example not working for me
Hi I really liked the video by Paul Lettieri, creating a replacement for HStack that gives equal width to each, so I wrote one for my app. But the app doesn't compile for me. It gives me "Trailing closure passed to parameter of type 'HorizontalAlignment' that does not accept a closure" against the top VStack, not the place where the equal lengths HStack replacement appears. This is my version of his struct: extension LayoutSubviews {       func maxSize() -> CGSize {     let subviewSizes = map { $0.sizeThatFits(.unspecified) }     return subviewSizes.reduce(CGSize.zero) { CGSize(width: Swift.max($0.width, $1.width), height: Swift.max($0.height, $1.height)) }   }// maxSize()       func spacing() -> [Double] {     return indices.map { index in       guard index < count - 1 else { return 0.0 }       return self[index].spacing.distance(to: self[index + 1].spacing, along: .horizontal)     }   }// spacing()     }// extension LayoutSubviews struct EqualWidthHStack: Layout {       func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {     let maxsize = subviews.maxSize()     let totalSpacing = subviews.spacing().reduce(0) { $0 + $1 }     return CGSize(width: maxsize.width * Double(subviews.count) + totalSpacing, height: maxsize.height)   }// sizeThatFits       func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {     let maxsize = subviews.maxSize()     let spacing = subviews.spacing()     let sizeProposal = ProposedViewSize(width: maxsize.width, height: maxsize.height)     var x = bounds.minX + maxsize.width / 2     for index in subviews.indices {       subviews[index].place(at: CGPoint(x: x, y: bounds.midX), anchor: .center, proposal: sizeProposal)       x += maxsize.width + spacing[index]     }   }// placeSubviews     }// EqualWidthHStack I wrote this trivial View to test it: struct ContentView: View {   var body: some View {     VStack {       HStack {         Button("Hi") { print("Hi!") }         Button("Hello") { print("Hello!") }         Button("Good evening") { print("Good evening!") }       }       EqualWidthHStack {         Button("Hi") { print("Hi!") }         Button("Hello") { print("Hello!") }         Button("Good evening") { print("Good evening!") }       }       Image(systemName: "globe")         .imageScale(.large)         .foregroundColor(.accentColor)       Text("Hello, world!")     }   } } I'm using Version 14.0 beta 2 (14A5229c) of Xcode. I was having a problem with the exact same message in another app with a much more complex main view. I hope someone can tell me what to do or what is going on here. Regards, Mark
Replies
5
Boosts
1
Views
2.1k
Activity
Feb ’24
Subviews don't apply assigned size in custom EqualSizeZStack layout
I tried to modify CustomLayoutProject in order to create some kind of ZStack which resizes child views to size of the largest one and positions them one over another with some vertical offset. And I'm stuck. Setup is next: I've created EqualSizeZStack: Layout and put in instead of ButtonStack() in ContentView like this: EqualSizeZStack { Buttons() }.border(.blue) I've restricted buttons width by setting their text width .frame(width: 200) instead of .frame(maxWidth: .infinity). Updated Goldfish model text to be 4x longer, so related button can grow up in restricted width (and it does). Layout container gets requested height, and largest size is calculated and assigned correctly (this logic was not changed). But smaller buttons just refuse to apply largest button size no matter what I do. What did I miss? func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) { guard !subviews.isEmpty else { return } let maxSize = maxSize(subviews: subviews) let placementProposal = ProposedViewSize(width: maxSize.width, height: maxSize.height) var nextY = bounds.minY for index in subviews.indices { subviews[index].place( at: CGPoint(x: bounds.midX, y: nextY), anchor: .top, proposal: placementProposal) nextY += vOffset } }
Replies
0
Boosts
0
Views
518
Activity
Apr ’23
Growing entries to natural width of column in SwiftUI Grid
The new Grid View looks like it brings us a simple way to get row-column layouts into our SwiftUI apps! I wonder if someone can help me with one aspect: I have a situation similar to the one described for the Pet Leaderboard App in the "Compose custom layouts with SwiftUI" video. But I want to save space by putting the Cat/Goldfish/Dog voting buttons in the leaderboard table itself, in the first column, instead of the labels. Here's the code for a simplified version of that table: import SwiftUI struct TableDemo: View { var body: some View { Grid(alignment: .leading) { GridRow { Button { // vote for Cat } label: { Text("Cat").fontWeight(.bold).foregroundColor(.white).padding().background(.green).cornerRadius(8) } Text("23").fontWeight(.bold).foregroundColor(.white).gridColumnAlignment(.trailing) } GridRow { Button { // vote for Goldfish } label: { Text("Goldfish").fontWeight(.bold).foregroundColor(.white).padding().background(.green).cornerRadius(8) } Text("3").fontWeight(.bold).foregroundColor(.white) } } .padding(20) .background(Color(red: 50.0/255, green: 85.0/255, blue: 125.0/255)) .cornerRadius(16) } } struct TableDemo_Previews: PreviewProvider { static var previews: some View { TableDemo() } } which looks like this: That result is visually unappealing because the buttons are inconsistent. It's nice that the Grid column is as wide as the widest button - that part is a big win from using the Grid View! But I'd like to get them to conform to the width of the widest button. The normal solution for some situations is to set an infinite maxWidth to the buttons. eg: Text("Cat").fontWeight(.bold).frame(maxWidth: .infinity).foregroundColor(.white).padding().background(.green).cornerRadius(8) But that results in the entire column expanding to take all available space: So what can we do, other than resorting to a PreferenceKey based approach, to solve this? Is there an official Grid-centric way of fixing this? This is the desired layout (I manually sized the buttons):
Replies
0
Boosts
0
Views
784
Activity
Oct ’22