1

I have a simple SwiftUI example that isn't behaving as expected.

What I expect to happen: When the button is clicked, the sheet should show a random number (text).

What happens instead: The first time, the sheet shows foo. In subsequent times, a number appears.

Why doesn't a number appear the first time?


ContentView.swift

import SwiftUI

struct ContentView: View {
    @State var showSheet = false
    @State var text = "foo"


    var body: some View {
        VStack {
            Text("Hello")
            PillButton(title: "Start Test", action: {
                print("Button tapped")
            }, text: $text, showSheet: $showSheet)
        }.sheet(isPresented: $showSheet, content: {
            Text(text)
        })
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

PillButton.swift

import SwiftUI

struct PillButton: UIViewRepresentable {
    let title: String
    let action: () -> ()
    @Binding var text: String
    @Binding var showSheet: Bool

    var pillButton = UIButton()

    func makeCoordinator() -> Coordinator { Coordinator(self) }

    class Coordinator: NSObject {
        var parent: PillButton

        init(_ pillButton: PillButton) {
            self.parent = pillButton
            super.init()
        }

        @objc func doAction(_ sender: Any) {
            let number = Int.random(in: 0..<100)
            self.parent.text = String(number)
            self.parent.action()
            self.parent.showSheet = true
        }
    }

    func makeUIView(context: Context) -> UIButton {
        let button = UIButton(type: .system)
        button.setTitle(self.title, for: .normal)
        button.addTarget(context.coordinator, action: #selector(Coordinator.doAction(_ :)), for: .touchDown)
        return button
    }

    func updateUIView(_ uiView: UIButton, context: Context) {}
}

jaynp
  • 3,275
  • 4
  • 30
  • 43

1 Answers1

1

Use ViewModel (ObservableObject) class. In your case struct view captured the initial value. so use ObservableObject class for referencing the value.

Here is the possible solution.

class ContentViewModel: ObservableObject{
    @Published var text = "foo"
}
struct ContentView: View {
    @State var showSheet = false
    @ObservedObject var viewModel = ContentViewModel()
    
    var body: some View {
        VStack {
            Text("Hello")
            PillButton(title: "Start Test", action: {
                print("Button tapped")
            }, text: $viewModel.text, showSheet: $showSheet)
        }.sheet(isPresented: $showSheet, content: {
            Text(self.viewModel.text)
        })
    }
}
Raja Kishan
  • 16,767
  • 2
  • 26
  • 52
  • Thanks! This works. Can you explain why the other behavior doesn't work like I expect? – jaynp May 17 '21 at 19:02
  • I found this guide useful to clear up some confusion: https://www.hackingwithswift.com/quick-start/swiftui/whats-the-difference-between-observedobject-state-and-environmentobject – jaynp May 18 '21 at 20:01