Make a Grid of Buttons of Same Width and Height in Swiftui

Make a grid of buttons of same width and height in SwiftUI

How about using a GeometryReader(docs here)? They give the dimensions of their parent view to the children. Here's a partial implementation based on what you already have:

import SwiftUI

struct ContentView : View {
var body: some View {
VStack {
Text("0")
NumpadView()
}
}
}

struct NumpadView : View {

let rows: Length = 5
let columns: Length = 4
let spacing: Length = 10

var horizontalEdges: Length {
return columns - 1
}

var verticalEdges: Length {
return rows - 1
}

func getItemWidth(containerWidth: Length) -> Length {
return (containerWidth - spacing * horizontalEdges) / columns
}

func getItemHeight(containerHeight: Length) -> Length {
return (containerHeight - spacing * verticalEdges) / rows
}

var body: some View {
GeometryReader { geometry in

VStack(alignment: .center, spacing: self.spacing) {
HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("7")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}

Button(action: {}) {
Text("8")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}

Button(action: {}) {
Text("9")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
}

HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("4")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}

Button(action: {}) {
Text("5")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}

Button(action: {}) {
Text("6")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
}

HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("1")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}

Button(action: {}) {
Text("2")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}

Button(action: {}) {
Text("3")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.green)
}
}

HStack(alignment: .center, spacing: self.spacing) {
Button(action: {}) {
Text("0")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width) * 2 + self.spacing, height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}

Button(action: {}) {
Text(".")
.frame(width: self.getItemWidth(containerWidth: geometry.size.width), height: self.getItemHeight(containerHeight: geometry.size.height))
.background(Color.yellow)
}
}

}.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
static var previews: some View {
ContentView()
}
}
#endif

Looks like this: Live preview

How to make group of buttons same width in SwiftUI

I'm doing something similar with an audio player also. My method is just to place an invisible (.opacity(0.0)) duplicate of the largest view in a ZStack so that the size doesn't jump.

ZStack {
Image(systemName: "larger-button-image")
.opacity(0.0)
Image(systemName: "smaller-button-image")
}

How to set UIImage's height to always be the same as the width?

If your image is already of a square aspect ratio you can just make it resizable, and select aspectRatio to fit.

Image(systemName: "plus")
.resizable()
.aspectRatio(contentMode: .fit)

You can delete .frame(maxWidth: .infinity) because it is intrinsic when you use resizable.
This is the effect obtained :

Sample Image

Obviously if your source Images are not squared, you have to specify the aspect ratio that you want.

UPDATE :

If you want a square colored background with a smaller image in the middle you can't do it directly from an Image object like you did in the Example, but you need a more complex object like this :

struct WidgetView : View
{
var backgroundColor : Color
var imageName : String

var body : some View
{
ZStack {
Rectangle()
.fill(backgroundColor)
.cornerRadius(10)
.aspectRatio(contentMode: .fit)
Image(systemName: imageName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxWidth: 50)
.font(.system(size: 30))
}
.padding(5)
}
}

The usage is very simple :

ForEach(0 ... 6, id: \.self) {
WidgetView(backgroundColor: colors[$0 % colors.count], imageName: "plus")
}

And the obtained effect is the following :

Sample Image

Making Button span across VStack

The best way to do this is via .frame(maxWidth: .infinity)
https://developer.apple.com/documentation/swiftui/view-layout

If you want the button not to be centered you need to specify alignment.

e.g.: .frame(maxWidth: .infinity, alignment: .leading)

Button(action: handleSignInAction) {
Text("Sign In")
}
.frame(maxWidth: .infinity)
.background(Color.green)

Old answer from 2019:

You could use a HStack with a Text and Spacer to get a Button that fills the width of its parent:

Button(action: handleSignInAction) {
HStack {
Spacer()
Text("Sign In")
Spacer()
}
}.background(Color.green)

Uniform cell size in SwiftUI LazyGrid

The cells themselves are actually the same size. You'll notice that no item spills over into the area of another row. What you have here, however, is that the content inside the cells is hugging to it's intrinsic size, rather than occupying as much of the space as it can.

I have not tested this, but one thing you can try is to wrap it all in a GeometryReader and then use the proxy size to explicitly set your own size:

GeometryReader { proxy in
Button(...)
.frame(width: proxy.size.width, height: proxy.size.height)
}

I'm not personally a fan of this one, so you can also try using the "flexible" frame:

Button(...)
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)

How to make a rectangle's height the same height of a VStack

You need to know the GeometryReader and PreferenceKey to make this possible.

struct SiblingHeightKey: PreferenceKey {
static var defaultValue: CGSize? {
nil
}

static func reduce(value: inout CGSize?, nextValue: () -> CGSize?) {
value = value ?? nextValue()
}
}

struct TodoView: View {
@State var vStackSize: CGSize? = nil
@State var todos = ["feed the dog", "take the dog out for a walk", "make coffee"]
@State var height: CGFloat = 45
var body: some View {
HStack{
RoundedRectangle(cornerRadius: 2)
.foregroundColor(.gray)
.frame(width: self.vStackSize?.width, height: self.vStackSize?.height)
VStack{

Text("Todo")
.font(.title)
ForEach(todos, id: \.self){ todo in
Text(todo)
}
}.background(
GeometryReader { proxy in
Color.clear.preference(key: SiblingHeightKey.self, value: proxy.size)
}
)

Spacer()
}.onPreferenceChange(SiblingHeightKey.self) {
self.vStackSize = $0
}
}
}


Related Topics



Leave a reply



Submit