Home > other >  SwiftUI PreferenceKey not Updating
SwiftUI PreferenceKey not Updating

Time:12-06

I'm struggling trying to implement a PreferenceKey. I've read countless articles, so obviously I'm missing something. onPreferenceChange reports once at simulator startup but not during scrolling. Scrolling updates the text in the TextEditor overlay, but the offsetCGSize property is apparently never updated. Here's a simplified version of the code:

struct SOView: View {

    @State private var firstText: String = "first"
    @State private var secondText: String = "second"
    @State private var thirdText: String = "third"

    @State private var offsetCGSize: CGSize = .zero

    var body: some View {
        ScrollView {
            VStack {
            
                ForEach(0..<20) {index in
                    Text("Line number \(index)")
                }
            
                Image(systemName: "globe")
                    .imageScale(.large)
                    .foregroundColor(.accentColor)
                TextField("First", text: $firstText)
            
                GeometryReader { teGeo in
                    TextEditor(text: $secondText)
                        .frame(height: 100)
                        .overlay(
                            RoundedRectangle(cornerRadius: 10).stroke(lineWidth: 2)
                        )
                        .overlay(
                            Text("\(teGeo.frame(in: .global).minY)")
                        )
                        .preference(key: OffsetPreferenceKey.self, value: CGSize(width: 0, height: teGeo.frame(in: .global).minY))
                }//geo
                .frame(height: 100)
            
                TextField("Third", text: $thirdText)
                Text("TextEditor top is \(offsetCGSize.height)")
            
            }//v
            .padding()
        
        }//scroll
        .onPreferenceChange(OffsetPreferenceKey.self) { value in
            offsetCGSize = value
            print("offsetCGSize is \(offsetCGSize)")
        }

    }//body
}//so view

private struct OffsetPreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = CGSize.zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}//off pref

Any guidance would be appreciated. Xcode 14.0.1, iOS 16

CodePudding user response:

For others - I don't understand why, but by moving the two TextFields, the Image and the reporting Text outside of the ScrollView I get the results I expect. (In this file, I changed the key name to OffsetPreferenceKey2)

enter image description here

struct SOView2: View {
    @State private var firstText: String = "first"
    @State private var secondText: String = "second"
    @State private var thirdText: String = "third"

    @State private var offsetCGSize: CGSize = .zero

    var body: some View {
        VStack {
        
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            TextField("First", text: $firstText)
                .padding(.horizontal)
        
            ScrollView {
                VStack {
                    ForEach(0..<20) {index in
                        Text("Line number \(index)")
                    }
                
                    GeometryReader { teGeo in
                        TextEditor(text: $secondText)
                            .frame(height: 100)
                            .overlay(
                                RoundedRectangle(cornerRadius: 10).stroke(lineWidth: 2)
                            )
                            .overlay(
                                Text("\(teGeo.frame(in: .global).minY)")
                            )
                            .preference(key: OffsetPreferenceKey2.self, value: CGSize(width: 0, height: teGeo.frame(in: .global).minY))
                    }//geo
                    .frame(height: 100)
                
                }//v
                .padding()
            }//scroll
            .onPreferenceChange(OffsetPreferenceKey2.self) { value in
                offsetCGSize = value
                print("offsetCGSize is \(offsetCGSize)")
            }// pref change
        
            TextField("Third", text: $thirdText)
                .padding(.horizontal)
            Text("TextEditor top is \(offsetCGSize.height)")
        
        }//outer v
    
    }//body
}//so view

private struct OffsetPreferenceKey2: PreferenceKey {
    static var defaultValue: CGSize = CGSize.zero
    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}//off pref
  • Related