The following code works perfectly fine on iOS, but not on iPadOS. When I tap on one of the items in the list, the corresponding detail view is shown, but it will not change if I tap on another item. When I change the model in the LanguageDetail
view to @ObservedObject
, it works. To be clear, this is only an example to illustrate the problem. In my actual project, I'm not able to make this change though. The code below demonstrates this problem.
struct ContentView: View {
let languages: [String] = ["Objective-C", "Java", "Python", "Swift", "Rust"]
@State var selectedLanguage: String?
var body: some View {
NavigationView {
List(languages, id: \.self) { language in
Button(action: {selectedLanguage = language}) {
Text(language)
.bold()
.padding()
}
}
.background {
NavigationLink(isActive: $selectedLanguage.isPresent()) {
if let lang = selectedLanguage {
LanguageDetail(model: LanguageDetailModel(languageName: lang))
} else {
EmptyView()
}
} label: {
EmptyView()
}
}
}
}
}
struct LanguageDetail: View {
@StateObject var model: LanguageDetailModel
var body: some View {
VStack {
Text(model.languageName)
.font(.headline)
Text("to rule them all...")
}
}
}
class LanguageDetailModel: ObservableObject {
@Published var languageName: String
init(languageName: String) {
self.languageName = languageName
}
}
This extension is needed:
/// This extension is from the [SwiftUI Navigation Project on Github](https://github.com/pointfreeco/swiftui-navigation)
extension Binding {
/// Creates a binding by projecting the current optional value to a boolean describing if it's
/// non-`nil`.
///
/// Writing `false` to the binding will `nil` out the base value. Writing `true` does nothing.
///
/// - Returns: A binding to a boolean. Returns `true` if non-`nil`, otherwise `false`.
public func isPresent<Wrapped>() -> Binding<Bool>
where Value == Wrapped? {
.init(
get: { self.wrappedValue != nil },
set: { isPresent, transaction in
if !isPresent {
self.transaction(transaction).wrappedValue = nil
}
}
)
}
}