4.9 C
New York
Monday, April 7, 2025

ios – SwiftUI align view to middle scrollView


My activity is to make a scrollview, which receives an array of strings and an overlay, which is situated within the middle. When some view will get into this overlay, it ought to instantly align to its borders, that’s, be within the middle of the display. The checklist ought to begin from the center of the display with the primary aspect.
I used to be on the lookout for an answer right here textual content

I’ve struct

struct Area: Equatable, Hashable {
    let title: String
}

PreferenceKeyStruct

struct ViewOffsetKey: PreferenceKey {
    static var defaultValue: [String: CGRect] = [:]
    
    static func cut back(worth: inout [String: CGRect], nextValue: () -> [String: CGRect]) {
        worth.merge(nextValue(), uniquingKeysWith: { $1 })
    }
}

and subView

struct RegionView: View {
    
    let title: String
    
    var physique: some View {
        HStack {
            Textual content(self.title)
                .foregroundColor(.white)
                .font(.system(.physique))
            
            Spacer()
        }
        .padding(16)
        .body(width: 348, alignment: .topLeading)
        .background(Shade.black)
        .cornerRadius(50)
        .id(title)
        .background(GeometryReader {
            Shade.clear.desire(key: ViewOffsetKey.self,
                                   worth: [name: $0.frame(in: .global)])
        })
    }
}

The primary view is realized by CurrentValueSubject and AnyPublisher.
I verify if subView intersects a rectangle with a top of 1, then I write it as displayed. When the scroll ends, I name the scrollTo methodology

struct RegionListView: View {
    let detector: CurrentValueSubject<[String: CGRect], By no means>
    let writer: AnyPublisher<[String: CGRect], By no means>
    
    let areas: [Region] = [
        Region(name: "Region 1"),
        Region(name: "Region 2"),
        Region(name: "Region 3"),
        Region(name: "Region 4"),
        Region(name: "Region 5"),
        Region(name: "Region 6"),
        Region(name: "Region 7"),
        Region(name: "Region 8"),
        Region(name: "Region 9"),
        Region(name: "Region 10"),
        Region(name: "Region 11"),
        Region(name: "Region 12"),
        Region(name: "Region 13"),
        Region(name: "Region 14"),
        Region(name: "Region 15"),
        Region(name: "Region 16"),
        Region(name: "Region 17"),
        Region(name: "Region 18"),
        Region(name: "Region 19"),
        Region(name: "Region 20"),
    ]
    
    @State personal var topVisibleChildId: String?
    
    init() {
        let detector = CurrentValueSubject<[String: CGRect], By no means>([:])
        self.writer = detector
            .debounce(for: .seconds(0.1), scheduler: DispatchQueue.major)
            .dropFirst()
            .eraseToAnyPublisher()
        self.detector = detector
    }
    
    var physique: some View {
        GeometryReader { geometryReader in
            ScrollViewReader { proxy in
                ScrollView {
                    VStack(spacing: 8) {
                        ForEach(self.areas, id: .self) { area in
                            RegionView(title: area.title)
                        }
                    }
                    .body(maxWidth: .infinity)
                    .onPreferenceChange(ViewOffsetKey.self) { childFrames in
                        detector.ship(childFrames)
                        
                        var visibleChildIds = [String]()
                        let screenMid = geometryReader.measurement.top / 2 + 56
                        
                        for (id, childFrame) in childFrames the place childFrame.intersects(CGRect(x: 0, y: Int(screenMid), width: .max, top: 56)) {
                            print("id (id) childFrame (childFrame)")
                            visibleChildIds.append(id)
                        }
                        
                        visibleChildIds.kind()
                        
                        if let first = visibleChildIds.first {
                            topVisibleChildId = first
                        }
                    }
                }
                .safeAreaPadding(.prime, geometryReader.measurement.top / 2 - 28)
                .safeAreaPadding(.backside, geometryReader.measurement.top / 2 - 28)
                .background(Shade.black)
                .onReceive(writer) { _ in
                    proxy.scrollTo(topVisibleChildId, anchor: .middle)
                }
                .overlay(
                    Textual content("High Seen Baby: (topVisibleChildId ?? "")")
                        .padding()
                        .background(Shade.blue.opacity(1))
                        .foregroundColor(.white)
                        .cornerRadius(10),
                    alignment: .prime
                )
                
                .overlay(
                    Rectangle()
                        .body(maxWidth: .infinity)
                        .body(top: 56)
                        .foregroundColor(Shade.clear)
                        .border(.inexperienced, width: 4),
                    alignment: .middle
                )
            }
        }
    }
}

My query: is it doable to implement a habits through which altering cells might be just like selecting a time when setting an alarm. That’s, the cell won’t must be scrolled to the middle if the consumer didn’t end scrolling within the center. I hope I defined it clearly.

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles