SwiftUI: @State modified but seemingly unchanged when read the line after

Viewed 98

I'm trying to build a button style in SwiftUI and I'd like to use the preference technique to retrieve the configuration label's size, as explained in this article.

But I have a weird issue where my code in onPreferenceChange which is supposed to change my size property just does not change it. You'll see this in sample code but I set the new value, and the line after I print the @State property and I don't get the value I just set!

import SwiftUI

struct MajorButtonStyle: ButtonStyle {
    
    @State private var labelSize: CGSize = .zero
            
    func makeBody(configuration: Configuration) -> some View {
        RoundedRectangle(cornerRadius: 10, style: .continuous)
            .fill(Color.accentColor)
            .overlay(
                configuration.label
                    .foregroundColor(.white)
                    .padding()
                    .modifier(SizeModifier())
                    .onPreferenceChange(SizePreferenceKey.self) { value in
                        print("PREFERENCE HAS CHANGED!!!!!")
                        print(value)
                        self.labelSize = value
                        print(value.height)
                        print(self.labelSize.height)
                    }
            )
            .frame(height: labelSize.height)
    }
    
}

// MARK: Modifier and preference key

struct SizePreferenceKey: PreferenceKey {
    static var defaultValue: CGSize = .zero

    static func reduce(value: inout CGSize, nextValue: () -> CGSize) {
        value = nextValue()
    }
}

struct SizeModifier: ViewModifier {
    private var sizeView: some View {
        GeometryReader { geometry in
            Color.clear.preference(key: SizePreferenceKey.self, value: geometry.size)
        }
    }

    func body(content: Content) -> some View {
        content.background(sizeView)
    }
}

Running the app and instantiating a button, here's the console output:

PREFERENCE HAS CHANGED!!!!!
(89.0, 52.33333333333333)
52.33333333333333
0.0 // 

Could someone tell me if I'm missing something? Or if I've fallen into some weird edge case, or bug? I'm testing this on Xcode 12 beta 2.

Thank you in advance.

1 Answers

What you try to do can be achieved in much simper way

struct MajorButtonStyle: ButtonStyle {
    func makeBody(configuration: Configuration) -> some View {
        configuration.label
            .frame(maxWidth: .infinity)    // << for all available width !!
            .foregroundColor(.white)
            .padding()
            .background(
                RoundedRectangle(cornerRadius: 10, style: .continuous)
                    .fill(Color.accentColor)
            )
    }
}

Note: btw, State wrapper works only in View, but ButtonStyle is not a view.

Related