The PresentationButton presents the view properly, but upon hitting "Cancel" to return to parent view, the button refuses to present the child view again.
Here is my PresentationButton code:
struct ContentView : View {
var body: some View {
ZStack {
PresentationButton(Text("Click to show"), destination: SomeOtherView())
.transition(.slide)
}
}
}
This bug has existed in various forms until Xcode 11 beta 4, but note that the API has changed.
PresentationButton became PresentationLink and then was deprecated in Xcode 11 beta 4 in favor of .sheet.
See here for an example of how to use the new functionality: https://stackoverflow.com/a/57087399/3179416
Related
Overview
On macOS, in a NavigationSplitView when the menu button is added to the detail view toolbar, it seems to look disabled (refer to steps to reproduce)
Question:
Is there a way to not look disabled (2nd time the sidebar cell is tapped)?
Environment
Xcode: 14.1 beta 3 (14B5033e)
macOS: 13.0 Beta (22A5365d)
Code:
struct ContentView: View {
#State private var selectedNumber: Int?
var body: some View {
NavigationSplitView {
List(0..<100, selection: $selectedNumber) { number in
Text("cell \(number)")
}
} detail: {
Text("Detail")
.toolbar {
ToolbarItem {
Menu {
Button("aa") {}
Button("bb") {}
} label: {
Label("Add Bookmark", systemImage: "book")
}
}
}
}
}
}
Steps to reproduce:
Run the project on macOS
Select cell 0 on the sidebar
Click on the book toolbar button and notice the menu appear
Select cell 1 on the sidebar
Expected Behaviour
After step 4, the book toolbar button should look prominent (like it is enabled)
Actual Behaviour
After step 4, the book toolbar button looks like it is disabled.
Screenshot
Feedback
I have filed a feedback, hope it gets fixed.
It might help if more feedbacks are filed as it might help get noticed
Since installing Xcode 14, I am now getting the following error message printed in my console:
NavigationLink presenting a value must appear inside a
NavigationContent-based NavigationView. Link will be disabled.
My app is structured as follows:
I have View A wrapped in a NavigationView. The Navigation View has a navigation link inside it that links to View B.
I have View B that doesn't have a Navigation View, but has a navigation link to View C. View B inherits the navigation view defined in View A
The warning is printed when I press the back button on View B, popping back to View A.
The warning goes away when I wrap View B in a NavigationView, but this of course now displays View B in two Navigation Views, which is not what I want.
I'm unsure why this warning is printing, because View B inherits the NavigationView defined in View A.
I had the same issue. Adding a check for iOS16 and using the new navigationstack if true fixed it for me.
WindowGroup {
if #available(iOS 16.0, *) {
NavigationStack {
ContentView()
}
} else {
// Fallback on earlier versions
NavigationView {
ContentView()
}
}
}
Will be deprecated in ios16,
The official documentation link is provided here, you can view the specific details
Deprecated
Use NavigationStack and NavigationSplitView instead. For more
information, see Migrating to new navigation types.
https://developer.apple.com/documentation/swiftui/navigationview
Try to add .navigationViewStyle(.stack) to the NavigationView. It helps in my case.
NavigationView {
// View A
}
.navigationViewStyle(.stack)
How to create such a view floating view with a custom boarder as shown in the picture? And such that it disappears as soon as the user clicks outside of the view.
Normally you would do that with a Popover like this:
#State var isPresented = false
var body: some View {
Button(action: {
self.isPresented = true
}) {
Text("Press me")
}.popover(isPresented: $isPresented, arrowEdge: .top) {
Text("Pop!") // You can put you own custom view here for the popover
}
}
Although it works as intended on the iPad (and I believe tvOS too, but I haven't tested it), it does not work properly with the current version of SwiftUI (as of 10/12/2019) on iPhones. Currently, the above code will just result in a somewhat glitchy modal on an iPhone, which I don't think is the intended function of it on iPhones. Apple's documentation for popover isn't very helpful right now, but here it is anyway.
For you information .popover is unabailable in tvOS.
I am creating an App where the login / register part is inside a modal, which is shown if the user is not logged in.
The problem is, that the user can dismiss the modal by swiping it down...
Is it possible to prevent this?
var body: some View {
TabView(selection: $selection) {
App()
}.sheet(isPresented: self.$showSheet) { // This needs to be non-dismissible
LoginRegister()
}
}
Second example:
I am using a modal to ask for information. The user should not be able to quit this process except by dismissing the modal with save button. The user has to input information before the button works. Unfortunately the modal can be dismissed by swiping it down.
Is it possible to prevent this?
iOS 15 and later:
Use .interactiveDismissDisabled(true) on the sheet, that's all.
Prev iOS 15:
You can try to do this by using a highPriorityGesture. Of course the blue Rectangle is only for demonstration but you would have to use a view which is covering the whole screen.
struct ModalViewNoClose : View {
#Environment(\.presentationMode) var presentationMode
let gesture = DragGesture()
var body: some View {
Rectangle()
.fill(Color.blue)
.frame(width: 300, height: 600)
.highPriorityGesture(gesture)
.overlay(
VStack{
Button("Close") {
self.presentationMode.value.dismiss()
}.accentColor(.white)
Text("Modal")
.highPriorityGesture(gesture)
TextField("as", text: .constant("sdf"))
.highPriorityGesture(gesture)
} .highPriorityGesture(gesture)
)
.border(Color.green)
}
}
This is a common problem and a "code smell"... well not really code but a "design pattern smell" anyway.
The problem is that you are making your login process part of the rest of the app.
Instead of presenting the LoginRegister over the App you should really be showing either App or LoginRegister.
i.e. you should have some state object like userLoggedIn: Bool or something and depending on that value you should show either App or LoginRegister.
Just don't have both in the view hierarchy at the same time. That way your user won't be able to dismiss the view.
If you dont mind using Introspect:
import Introspect
#available(iOS 13, *)
extension View {
/// A Boolean value indicating whether the view controller enforces a modal behavior.
///
/// The default value of this property is `false`. When you set it to `true`, UIKit ignores events
/// outside the view controller's bounds and prevents the interactive dismissal of the
/// view controller while it is onscreen.
public func isModalInPresentation(_ value: Bool) -> some View {
introspectViewController {
$0.isModalInPresentation = value
}
}
}
Usage:
.sheet {
VStack {
...
}.isModalInPresentation(true)
}
iOS 15+
Starting from iOS 15 you can use interactiveDismissDisabled.
You just need to attach it to the sheet:
var body: some View {
TabView(selection: $selection) {
App()
}.sheet(isPresented: self.$showSheet) {
LoginRegister()
.interactiveDismissDisabled(true)
}
}
Regarding your second example, you can pass a variable to control when the sheet is disabled:
.interactiveDismissDisabled(!isAllInformationProvided)
You can find more information in the documentation.
theoretically this may help you (I didn't tryed it)
private var isDisplayedBind: Binding<Bool>{ Binding(get: { true }, set: { _ = $0 }) }
and usage:
content
.sheet(isPresented: isDisplayedBind) { some sheet }
The way to achieve Master and Detail structure, as of beta 4, it should be by using .navigationViewStyle(.doubleColumn). Works perfectly on iOS / iPadOS / macOS but not in tvOS... it's a bug or I'm missing something?
import SwiftUI
struct ContentView: View {
var arra = ["Margherita","Marinara","Calzone"]
var body: some View {
NavigationView {
List(arra, id: \.self) { pizza in
NavigationLink(destination: SecondView(pizza: pizza)) {
Text(pizza)
}
}.navigationBarTitle("MASTER")
SecondView(pizza: arra[0])
}
.navigationViewStyle(.doubleColumn)
}
}
struct SecondView: View {
var pizza : String
var body: some View {
Text(pizza)
.navigationBarTitle("DETAIL")
}
}
(as of GM release) The official doc tells something like: double column style will be stacked on tvOS, like it's an iPhone in portrait mode. So it's impossible to automatically achieve the "master and detail" look like it's an iPad in landscape, you have to build it for yourself. Even if you use one UISplitViewController in UIKit! With this behavior I think it's good to go only if the master it's a fullscreen CollectionView, sorta like Netflix App.