Winabryr
Winabryr

Reputation: 13

Auto calculate padding to fill entire view's frame with SwiftUI

I'm almost sure that there is a simple solution, but after several years of using UIKit, it's not very obvious for me.

There are in fact a calendar with seven buttons to switch days of the week. I use a constant padding value between buttons, but I need to divide view's frame to seven equal parts, one for each button to adapt the layout for different screen sizes. It's a very easy task in frame layout, but I can't guess how to do this right with SwiftUI. Spacer() in this case look not very nice.

Some pictures to clear what's going on: what I want, what I have.

Thanks!

struct WeekCalendarView: View {

    @ObservedObject var selectedDay: ObservableDate

    private var days: [Date] {
        return selectedDay.date.currentWeek
    }

    var body: some View {
        HStack {
            ForEach(days, id: \.self) { day in
                Button(action: {
                    self.selectedDay.date = day
                }) {
                    VStack {
                        Text(day.date)
                            .fontWeight(.semibold)
                            .foregroundColor(day == self.selectedDay.date ? Color.white : Color.black)
                            .padding(14)
                            .background(day == self.selectedDay.date ? Color.green : Color.clear)
                            .clipShape(Circle())
                            .padding(.vertical, 6)

                        Text(day.dayOfTheWeek)
                            .fontWeight(.light)
                            .font(.caption)

                    }
                }
                .buttonStyle(PlainButtonStyle())
            }
        }
    }
}

Upvotes: 1

Views: 1161

Answers (2)

Chris
Chris

Reputation: 8091

Roland did not test his solution, so there were some mistakes in it, here is a tested solution based on Rolands answer:

changes: spacing has to be set to 0 in HStack, else the result is still ugly. And there was a small type "with" ...

struct ContentView: View {
    
    var days = [
        Day(date: "9", dayOfTheWeek: "Su"),
        Day(date: "10", dayOfTheWeek: "Mo"),
        Day(date: "11", dayOfTheWeek: "Tu"),
        Day(date: "12", dayOfTheWeek: "We"),
        Day(date: "13", dayOfTheWeek: "Th"),
        Day(date: "14", dayOfTheWeek: "Fr"),
        Day(date: "15", dayOfTheWeek: "Sa")

        ]
    
    @State var selectedDay : Day = Day(date: "10", dayOfTheWeek: "Mo")
    
    var body: some View {
        HStack(spacing:0) {
            ForEach(days, id: \.self) { day in
                Button(action: {
                    self.selectedDay = day
                }) {
                    VStack {
                        Text(day.date)
                            .fontWeight(.semibold)
                            .foregroundColor(day == self.selectedDay ? Color.white : Color.black)
                            .padding(14)
                            .background(day == self.selectedDay ? Color.green : Color.clear)
                            .clipShape(Circle())
                            .padding(.vertical, 6)
                        
                        Text(day.dayOfTheWeek)
                            .fontWeight(.light)
                            .font(.caption)
                        
                    }
                   .frame(width: screen.size.width / 7) // This is where I would use the screen size.

                }
                .buttonStyle(PlainButtonStyle())
            }
        }
    }
}

Upvotes: 0

Asperi
Asperi

Reputation: 257719

Here is a demo of automatic platform-independent layout (w/o padding) on replicated model.

Demo prepared & tested with Xcode 12 / iOS 14

demo1 demo

struct WeekCalendarView: View {

    private var days = [23, 24, 25, 26, 27, 28, 29]
    private var dayOfTheWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sut"]

    @State private var selectedDay = 24

    var body: some View {
        HStack {
            ForEach(0..<days.count, id: \.self) { day in
                Button(action: {
                    self.selectedDay = days[day]
                }) {
                    VStack {
                        Circle()
                            .fill(days[day] == self.selectedDay ? Color.green : Color.clear)
                            .aspectRatio(contentMode: .fit)
                            .overlay(
                                Text("\(days[day])")
                                    .fontWeight(.semibold).fixedSize()
                                    .foregroundColor(days[day] == self.selectedDay ? Color.white : Color.black)
                            )
                            .padding(.vertical, 6)
                            .frame(maxWidth: .infinity)

                        Text(dayOfTheWeek[day])
                            .fontWeight(.light)
                            .font(.caption)
                    }
                }
                .buttonStyle(PlainButtonStyle())
            }
        }
    }
}

Upvotes: 1

Related Questions