Home > Back-end >  SwiftUI: Avoid new instance with dependency injection
SwiftUI: Avoid new instance with dependency injection

Time:08-21

I'm using this answer to init an ObservableObject with the horizontalSizeClass: https://stackoverflow.com/a/73339015 . What this achieves is to set the blue rectangle to visible on regular sized devices and invisible on compact devices on init. So far so good.

The problem is that the Settings object will of course be re-initalized when the size class changes (for example when rotating an iPhone 13 Pro Max between portrait and landscape). For the user this can result in the blue rectangle appearing or disappearing. What I can't figure out is how I can init isVisible based on the horizontal size class once, but not create a new instance when the horizontal size class changes later to keep the current value of isVisible. Is this even possible?

Code:

struct ContentView: View {
    @Environment(\.horizontalSizeClass) var horizontalSizeClass

    struct MainView: View {
        @EnvironmentObject var settings: Settings

        var body: some View {
            VStack {
                Button("Toggle Visibility") {
                    settings.isVisible.toggle()
                }
                Rectangle()
                    .frame(width: 100, height: 100)
                    .foregroundColor(.blue)
                    .opacity(settings.isVisible ? 1 : 0)
            }
            .animation(.linear(duration: 2.0), value: settings.isVisible)
        }
    }

    var body: some View {
        MainView()
            .environmentObject(
                Settings(isVisible: horizontalSizeClass == .regular)
            )
    }
}

class Settings: ObservableObject {
    @Published var isVisible: Bool

    init(isVisible: Bool) {
        self.isVisible = isVisible
    }
}

CodePudding user response:

isVisible should init with a specific value depending on the size class, but then it should only be decided by the user if it is visible of not. It should not "automatically" change when the device is rotated.

then approach should be different - we will inject state of horizontalSizeClass as initial value and use initializable-once feature of StateObject...

here is main changed parts (tested with Xcode 13.4 / iOS 15.5)

// injection
var body: some View {
    MainView(isVisible: horizontalSizeClass == .regular)
}

// initialisation
struct MainView: View {
    @StateObject var settings: Settings

    init(isVisible: Bool) {
        _settings = StateObject(wrappedValue: Settings(isVisible: isVisible))
    }

everything else is same as before.

Test module is here

  • Related