Search code examples
swiftmacosswiftui

Reproduce apple macos calendar horizontal scroll


I am trying to reproduce the macos apple calendar but I'm having issues making the horizontal infinite scroll. The calendar needs to display 7 days at all times meaning that the user can't scroll at any position

This is my current code where I put a rectangle at the beginning of every day cell like this: actual calendar I already have a ScrollViewReader with the ids of every day cell but I can't get it to stop on the beginning of each day cell.

@State private var loadedDays = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun","Mon1", "Tue1", "Wed1", "Thu1", "Fri1", "Sat1", "Sun1"]
@State private var scrollPos: String?

var body: some View {
    GeometryReader { geomtry in
        Text("\(scrollPos ?? "nil")")
        ScrollViewReader { proxy in
            ScrollView (.horizontal) {
                LazyHStack(spacing: 10) {
                    ForEach($loadedDays, id: \.self) { day in
                        HStack {
                            Rectangle()
                                .frame(width: 5, height: 5, alignment: .leading)
                                .id("r" + day.wrappedValue)
                            VStack {
                                Text(day.wrappedValue)
                                
                            }
                            .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                            .id(day.wrappedValue)
                            
                            Rectangle()
                                .frame(width: 5, height: 5, alignment: .trailing)
                                .id("l" + day.wrappedValue)
                        }.border().frame(width: geomtry.size.width/7)

                    }
                }
                .padding()
                .scrollTargetLayout()
            }
            .defaultScrollAnchor(.center)
            .onChange(of: scrollPos) { old, new in
                if(new != nil && !new!.starts(with: "r")) {
                    withAnimation {
                        proxy.scrollTo("r" + new!, anchor: .leading)
                    }
                }
            }
        }
        .scrollPosition(id: $scrollPos)

    }
}

With this code sometimes it works but with no animation but most of the time it lets me scroll anywhere.


Solution

  • From the comment of Sweeper. I used the scrollTargetBehavior(.viewAligned) to solve the problem

            GeometryReader { geometry in
            ScrollViewReader { proxy in
                ScrollView (.horizontal) {
                    LazyHStack(spacing: 0) {
                        ForEach($loadedDays, id: \.self) { day in
                            HStack {
                                Rectangle()
                                    .frame(width: 5, height: 5, alignment: .leading)
                                VStack {
                                    Text(day.wrappedValue)
                                    
                                }
                                .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
                                
                                Rectangle()
                                    .frame(width: 5, height: 5, alignment: .trailing)
                            }
                            .border()
                            .frame(width: geometry.size.width/7)
                        }
                    }
                    .scrollTargetLayout()
                }
                //.defaultScrollAnchor(.center)
                .scrollTargetBehavior(.viewAligned)
                .defaultScrollAnchor(.leading)
            }