11

I try to fix the button width in swiftUI, but whether the width is pointed in .frame(width: ) or fixed in .fixedSize(horizontal: true, vertical: false) modifiers, or both, it still doesn't work. The button is just shrink or expand by its content length. How can I solve it?

Pls see below the code & picture:

struct ContentView: View {
    var body: some View {
        let days = [0, 1, 2, 3, 4]
        
        let dayWeatherList = ["Sunday, Sunny",
                                  "Monday, Sunny",
                                  "Tuesday, Sunny",
                                  "Wednesday, Sunny",
                                  "Thursday, Sunny"]
        
        let aqiList = [aqiItem(aqi: 6, color: .green),
                               aqiItem(aqi: 123, color: .orange),
                               aqiItem(aqi: 25, color: .green),
                               aqiItem(aqi: 345, color: .red),
                               aqiItem(aqi: 56, color: .yellow)]
        
        VStack {
            ForEach(days, id: \.self) { index in
                let dayWeatherInfo = dayWeatherList[index]
                let aqiForecast = aqiList[index]
                ForecastDayView(dayWeatherInfo: dayWeatherInfo, aqiForecast: aqiForecast)
            }
        }
        .padding(24)

    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct aqiItem {
    let aqi: Int
    let color: Color
}

struct ForecastDayView: View {
    let dayWeatherInfo: String
    let aqiForecast: aqiItem
    
    var body: some View {
        let fontSize: CGFloat = 14
        
        HStack(alignment: .center) {
            Text(dayWeatherInfo)
                .font(.system(size: fontSize))
                .fontWeight(.semibold)
                .foregroundColor(.black)
            
            Spacer()
            
            Text("24/32°")
                .font(.system(size: fontSize))
                .fontWeight(.semibold)
                .foregroundColor(.black)
            
            Spacer()
            
            HStack(spacing: 16) {
                if let aqiForecast = aqiForecast {
                    let aqi = aqiForecast.aqi
                    let color = aqiForecast.color
                    
                    Button(action: {}, label: {
                        Text("\(aqi)")
                            .foregroundColor(.white)
                            .padding(EdgeInsets(top: 2, leading: 4, bottom: 2, trailing: 4))
                    })
                    .font(.system(size: 13))
                    .background(color)
                    .cornerRadius(4)
                    .frame(width:40)
                    .fixedSize(horizontal: true, vertical: false)
                }
                
                let length: CGFloat = 18
                
                Image("100")
                    .resizable()
                    .frame(width: length, height: length)
                
                Image("101")
                    .resizable()
                    .frame(width: length, height: length)
            }
            
        }
    }
    
}

Buttons have different width: enter image description here

Thanks very much in advance!

1
  • Have you tried putting the Button label in an HStack? If you put Spacers on either side, the text will stay centered, and you can control the width of the HStack. Commented Dec 3, 2020 at 14:21

3 Answers 3

17

You are applying .background before you set the width. If you first apply .frame(width: 40) and then set the background color, you will see everything has the same size.

Button(action: {}, label: {
    Text("\(aqi)")
        .foregroundColor(.white)
        .padding(EdgeInsets(top: 2, leading: 4, bottom: 2, trailing: 4))
})
.frame(width:40)
.font(.system(size: 13))
.background(color)
.cornerRadius(4)
Sign up to request clarification or add additional context in comments.

Comments

2

Order of modifiers is important. This link may help you.

https://www.hackingwithswift.com/books/ios-swiftui/why-modifier-order-matters


Do as davidev has suggested or use .frame(minWidth:40) on Text and remove its horizontal padding.

                Button(action: {}, label: {
                    Text("\(aqi)")
                        .foregroundColor(.white)
                        .padding(.vertical, 2)
                        .frame(minWidth:40)
                })
                .font(.system(size: 13))
                .background(color)
                .cornerRadius(4)

Comments

0

You can keep it look cleaner by doing it in the following way:

    struct ContentView: View {
    var body: some View {
        let days = [0, 1, 2, 3, 4]
        
        let aqiList = [aqiItem(aqi: 6, color: .green),
                       aqiItem(aqi: 123, color: .orange),
                       aqiItem(aqi: 25, color: .green),
                       aqiItem(aqi: 345, color: .red),
                       aqiItem(aqi: 56, color: .yellow)]
        
        let dayWeatherList = ["Sunday, Sunny",
                              "Monday, Sunny",
                              "Tuesday, Sunny",
                              "Wednesday, Sunny",
                              "Thursday, Sunny"]
        
        VStack {
            ForEach(days, id: \.self) { index in
                let dayWeatherInfo = dayWeatherList[index]
                let aqiForecast = aqiList[index]
                ForecastDayView(dayWeatherInfo: dayWeatherInfo, aqiForecast: aqiForecast)
            }
        }
        .padding(24)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct aqiItem {
    let aqi: Int
    let color: Color
}

struct ForecastDayView: View {
    let dayWeatherInfo: String
    let aqiForecast: aqiItem
    let length: CGFloat = 18
    
    private var images: some View {
        HStack { // Can use Group as well
            Image("100")
                .resizable()
                .frame(width: length, height: length)
            
            Image("101")
                .resizable()
                .frame(width: length, height: length)
        }
    }
    
    private var forecastView: some View {
        HStack { // Can use Group as well
            VStack {
                Text(dayWeatherInfo)
                    .font(.system(size: 14))
                    .fontWeight(.semibold)
                    .foregroundColor(.black)
            }
            .frame(minWidth: 0, maxWidth: .infinity)
            
            VStack {
                Text("24/32°")
                    .font(.system(size: 14))
                    .fontWeight(.semibold)
                    .foregroundColor(.black)
            }
            .frame(minWidth: 0, maxWidth: .infinity)
        }
    }
    
    var body: some View {
        HStack(spacing: 0) {
            
            forecastView[![enter image description here][1]][1]
            
            if let aqiForecast = aqiForecast {
                let aqi = aqiForecast.aqi
                let color = aqiForecast.color
                VStack {
                    Button(action: {}, label: {
                        Text("\(aqi)")
                            .foregroundColor(.white)
                            .padding(EdgeInsets(top: 2, leading: 4, bottom: 2, trailing: 4))
                    })
                    .font(.system(size: 13))
                    .cornerRadius(4)
                }
                .frame(minWidth: 0, maxWidth: .infinity)
                .background(color)
            }
            
            images
        }
    }
}

enter image description here

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.