ios – SwiftUI align view to heart scrollView

ios – SwiftUI align view to heart scrollView


My process is to make a scrollview, which receives an array of strings and an overlay, which is positioned within the heart. When some view will get into this overlay, it ought to instantly align to its borders, that’s, be within the heart of the display screen. The checklist ought to begin from the center of the display screen with the primary ingredient.
I used to be in search of 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(Colour.black)
        .cornerRadius(50)
        .id(title)
        .background(GeometryReader {
            Colour.clear.choice(key: ViewOffsetKey.self,
                                   worth: [name: $0.frame(in: .global)])
        })
    }
}

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

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 non-public var topVisibleChildId: String?
    
    init() {
        let detector = CurrentValueSubject<[String: CGRect], By no means>([:])
        self.writer = detector
            .debounce(for: .seconds(0.1), scheduler: DispatchQueue.primary)
            .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.dimension.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.type()
                        
                        if let first = visibleChildIds.first {
                            topVisibleChildId = first
                        }
                    }
                }
                .safeAreaPadding(.high, geometryReader.dimension.top / 2 - 28)
                .safeAreaPadding(.backside, geometryReader.dimension.top / 2 - 28)
                .background(Colour.black)
                .onReceive(writer) { _ in
                    proxy.scrollTo(topVisibleChildId, anchor: .heart)
                }
                .overlay(
                    Textual content("High Seen Baby: (topVisibleChildId ?? "")")
                        .padding()
                        .background(Colour.blue.opacity(1))
                        .foregroundColor(.white)
                        .cornerRadius(10),
                    alignment: .high
                )
                
                .overlay(
                    Rectangle()
                        .body(maxWidth: .infinity)
                        .body(top: 56)
                        .foregroundColor(Colour.clear)
                        .border(.inexperienced, width: 4),
                    alignment: .heart
                )
            }
        }
    }
}

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

Leave a Reply

Your email address will not be published. Required fields are marked *