Display a Custom Alert | raywenderlich.com


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/18176818-your-first-ios-and-swiftui-app-polishing-the-app/lessons/27
1 Like

In ContentView.swift we check to see if alertIsVisible == true before we call PointsView(). So if I understand correctly, there is no need to pass-in $alertIsVisible to the method because that particular value that is being passed-in is not used by the method. It only sets alertIsVisible back to false.
However, as I’m typing this I think that I have answered my own question
 the passing-in of the alertIsVisible var is necessary so that the new false value that PointsView() sets is also set to the binding within the view and thus, that new false value is applied to the state variable of the same name in ContentView.swift

@webdevbyalex You’ve got it right. Here’s another way to think of it:

  • In SwiftUI, every piece of state data should have a “source of truth / owner”. For the alertIsVisible state, the “source of truth / owner” is ContentView. We can clearly see this because it’s marked as @State in ContentView.swift.
  • In SwiftUI, whenever you want another view to access a piece of state data owned elsewhere, you make a binding to that state data (which I sometimes like to think of as a “reference”). In this case, we want PointsView to access the alertIsVisible, which is owned by ContentView. So, we make a @Binding in ContentView.swift to store a reference to alertIsVisible (which happens to have the exact same name, but we could have given it a different name). Then in ContentView, we use $ to convert the state variable to a binding, and pass that to our new PointsView.
  • At this point, the alertIsVisible in ContentView and PointsView are the pointing to the same piece of data, so if we set it to false in one place, it’s false in the other place, etc.

I hope this makes sense!

Thanx for that thorough explanation Ray!

1 Like

I’m seing a strange behaviour with the ternary conditional operator.
I’m using it exactly the way Ray is doing that.

InstructionsView(game: $game)
.padding(.bottom, alertIsVisible ? 0 : 100)

If I resume the preview it gives me the following error:
"Compiling failed: result values in ‘? :’ expression have mismatching types ‘Int’ and ‘CGFloat’
If I build and run the project in the simulator everything is fine.
I changed the code to:

InstructionsView(game: $game)
.padding(.bottom, alertIsVisible ? CGFLoat(0) : 100)

Then the preview is working correctly.
I’m using Xcode 12.5 (12E262)
I got the same issue in the next lesson with shapes.swift

1 Like

Yep I see the same behavior. As of Xcode 12.5, you now need to manually cast Ints to CGFloats / etc. as you’ve done here.

For more information, see this thread (@zhongdongy has a nice explanation): SwiftUI Views | raywenderlich.com - #6 by zhongdongy

You give me the answer! Thanks!

Then sliderValue in PointsView.swift can detach @Binding ?
just let sliderValue: Double

I am getting an error that I can’t seem to find the solution for. In this part of the code, I get the error “Cannot convert value of type ‘Binding’ to expected argument type ‘Binding’”.

static var previews: some View {
        PointsView(alertIsVisible: alertIsVisible, sliderValue: sliderValue, game: game)
        PointsView(alertIsVisible: alertIsVisible, sliderValue: sliderValue, game: game)
            .previewLayout(.fixed(width: 568, height: 320))
        PointsView(alertIsVisible: alertIsVisible, sliderValue: sliderValue, game: game)
            .preferredColorScheme(.dark)
        PointsView(alertIsVisible: alertIsVisible, sliderValue: sliderValue, game: game)
            .preferredColorScheme(.dark)
            .previewLayout(.fixed(width: 568, height: 320))
    }