SwiftUI: LazyVGrid, GridItem filling width - ios

I am trying to create a grid with a known number of columns. For this example the number of columns is 3.
I would like the first column to fill the space available, but it looks like it only fills 1/3 of the space available.
struct ContentView: View {
var body: some View {
LazyVGrid(columns: self.columns, alignment: .leading, spacing: 10) {
Group {
Text("Fill")
.frame(maxWidth: .infinity)
Text("Adapt")
Text("Adapt")
}
.background(Color.red)
}
}
var columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible())
]
}
Result:
But I would like the first column to fill all the available space, while the last 2 columns are pushed to the right side.
Is this possible with a LazyVGrid?. Of course without having to specify frame sizes manually. Thanks!

Related

SwiftUI - Text elements not starting from same leading position

I am still a beginner in swiftUI.
Adding code and screenshot which will have explain my problem better. In the below screenshot, how can I align 'E' of first line with 'T' of 2nd line. I want both 'Text' elements to have same leading space i.e. both should start from the same position from left.
I have seen cases where people are using just one Text element and '\n' to move text to the next line and that aligns the text, but in my case I will be having more elements such as TextField and some more Text elements below these 2 texts hence I can't use the '\n' idea.
Code:
struct TestData: View {
var body: some View {
ZStack {
Image("backgroundImage").resizable().edgesIgnoringSafeArea(.all).scaledToFill()
VStack(alignment: .leading, spacing: 10) {
Text("Enter your Data")
.font(.largeTitle).bold()
.frame(width: UIScreen.main.bounds.width-50, height: 33.0)
Text("This is a very very long text to wrap on the next line. This text is of 2 lines.")
.font(.callout)
.frame(width: UIScreen.main.bounds.width-50, height: 80.0)
.foregroundColor(.gray)
.lineLimit(nil)
HStack {
// Contains image and textfield. Data will be entered in textfield.
}
// Move Text and Button elements.
}.offset(x: -10, y: -100)
}
}
}
struct TestData_Previews: PreviewProvider {
static var previews: some View {
TestData()
}
}
Screenshot:
In general, don't set fixed frames; SwiftUI tends to work better when you let the layout engine do its thing. Using maxWidth/maxHeight and minWidth/minHeight can be useful for giving clues to the layout engine about what you want.
Similarly with offset - This moves things around but doesn't change their layout bounding box, so you can end up with overlapping elements (which is fine if that is what you want).
For your layout, you can simply remove the frame and offset and use some padding to shift everything in from the leading edge:
VStack(alignment: .leading, spacing: 10) {
Text("Enter your Data").font(.largeTitle)
.bold()
Text("This is a very very long text to wrap on the next line. This text is of 2 lines.").font(.callout)
.foregroundColor(.gray)
.lineLimit(nil)
HStack {
// Contains image and textfield. Data will be entered in textfield.
}
}.padding(.leading,50)

How to get a grid in SwiftUI with custom layout (different cell size)

So I am trying to get a custom grid with cells of different size. I have kinda gotten stuck here. So I have one particular cell with the height and size double that of all the others. But the left side of the cell requires two rows instead of one. How can I force to add two more boxes below H3 or to left of H4 box in the picture below.
Here is my code:
struct CustomGridView: View {
var body: some View {
let gridItems = [GridItem(.fixed(150), spacing: 10, alignment: .leading),
GridItem(.fixed(150), spacing: 10, alignment: .leading),
GridItem(.fixed(150), spacing: 10, alignment: .leading)]
LazyVGrid(columns: gridItems, spacing: 10) {
ForEach(0..<9) { g in
Text("H:\(g)")
.frame(width: g == 4 ? 310 : 150, height: g == 4 ? 310 : 150)
.background(Color.red)
if g == 4 { Color.clear }
}
}
.frame(width: 470)
}
}
Here is the screen shot of the grid situation so far:
Any help with this will be appreciated.
I don't think you can do that when just using lazy vgrid ForEach... Perhaps you can do something like that if you check for highest cell height in a row and then somehow fill it with two more boxes. You'd need to write a custom function for that. Also which block will go into the blank space above/below H:3? You'd need to write some custom logic for that.

How can I remove the minimum vertical padding or minimum height of a List row in SwiftUI?

I am trying to add a very short row to my list with a frame height of 2 but the list is adding an undesired padding above and below the list item or constraining to a minimum height and I don't know how to remove it. I have produced the following to illustrate the problem:
struct ContentView: View {
var body: some View {
List {
Color.red
.frame(height: 2)
Color.blue
.frame(height: 2)
}
}
}
How can I remove this padding above and below the list item? It's the same if using a section and adding header or footer. It will not allow me to have a list entry with small frame height.
In a LazyVStack I don't have this problem but I need to use a list.
Use .environment(\.defaultMinListRowHeight, 0) to make each row have a minimum height of 0. Otherwise, each row will always be at least 44 (the default) points high.
Use .listRowInsets(EdgeInsets()) to make each row have no inset. You could also do .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)), which results in the same.
struct ContentView: View {
var body: some View {
List {
Color.red
.frame(height: 5)
.listRowInsets(EdgeInsets()) /// 2.
Color.blue
.frame(height: 5)
.listRowInsets(EdgeInsets())
}
.environment(\.defaultMinListRowHeight, 0) /// 1.
}
}
Result:

Match UIStackView's .fill alignment with SwiftUI VStack

I've got a VStack with 3 Texts in it. Each has a different length string. I'd like the VStack's width to be just big enough to fit the widest Text, but I'd also like all three Texts to fill the VStack horizontally.
By default, with this code:
VStack(spacing: 4.0) {
ForEach(1..<4) {
Text(Array<String>(repeating: "word", count: $0).joined(separator: " "))
.background(Color.gray)
}
}
I get:
I want:
In UIKit, I could do this with a vertical UIStackView whose alignment property was set to .fill. VStack doesn't have a .fill alignment. The suggested solution I've seen is to modify the frame of each child of the stack view (ie. each Text) with .infinity for maxWidth.
The suggestion I've found is to modify the Texts with .frame(maxWidth: .infinity). However, this makes the whole VStack expand to (presumably) its maximum size:
Is there a way to make the VStack naturally grow to the size of its widest child, and no larger, while making all its children the same width?
Just add .fixedSize().
struct ContentView: View {
var body: some View {
VStack(spacing: 4.0) {
ForEach(1..<4) {
Text(Array<String>(repeating: "word", count: $0).joined(separator: " "))
.frame(maxWidth: .infinity) /// keep the maxWidth
.background(Color.gray)
}
}
.fixedSize() /// here!
}
}
Result:

SwiftUI LazyVGrid how to size a column to use the greatest width needed to display all its text

I want to display data in a grid that has a row header with text of different lengths.
The width of the first column should be just large enough to fit the longest text.
The remaining columns should be equally distributed.
The following sample can be used to illustrate the problem:
struct ContentView: View
{
private var firstColumn: GridItem { GridItem(alignment: .trailing) }
private var remainingColumns: [GridItem] { [GridItem(alignment: .center), GridItem(alignment: .center)] }
private var columns: [GridItem] { [firstColumn] + remainingColumns }
var body: some View {
LazyVGrid(columns: columns, alignment: .leading) {
ForEach(1..<5) {row in
// Row Header
Text("Row \(row)")
// remaining columns
ForEach(1..<3) {col in
Text("\(row),\(col)")
}
}
}
}
}
However, for GridItems I can only specify a fixed width or Minimum and Maximum width, but not something like "optimal" width.
Any ideas how to do this?

Resources