Status bar disappears after unlocking with SwiftUI - ios

I have an app with Status Bar initially hidden: YES and
NavigationView {
}
.statusBar(hidden: true)
When the app starts, the bar is not visible but its space is not occupied (so it looks like additional padding from top). But when I lock/unlock the phone this padding disappears thus moving the whole app closer to the top.
Any suggestions what's causing it?

Problem
Just tested it with the following view and it really jumps up after you lock/unlock the screen. Try to take a screenshot and it jumps up as well (found this accidentally when I was taking screenshots for the answer).
struct ContentView: View {
var body: some View {
NavigationView {
Color.green
.navigationBarTitle("No status bar")
}
.statusBar(hidden: true)
}
}
Workaround
Just add the line marked in the code below.
struct ContentView: View {
var body: some View {
NavigationView {
Color.green
.navigationBarTitle("No status bar")
}
.edgesIgnoringSafeArea(.all) // <-------
.statusBar(hidden: true)
}
}
IMHO it should work without this line, but it doesn't apparently. This problem is here even if I set (Info.plist):
UIViewControllerBasedStatusBarAppearance to YES
UIStatusBarHidden to YES
Or if I hide status bar directly on the ContentView (SceneDelegate):
let contentView = ContentView().statusBar(hidden: true)

Related

SwiftUI: strange offset on tap when reopening app with a `sheet` open

I am facing out a strange behavior that really looks like a SwiftUI bug.
When I leave the app with a .sheet open and reopen it, all content from parent has an offset on tap. It is difficult to explain (and English is not my mother tongue) so here is a really simple example:
struct ContentView: View {
#State private var isOpen = false
var body: some View {
Button(action: {
isOpen.toggle()
}, label: {
Text("Open sheet")
.foregroundColor(.white)
.padding()
.background(.blue)
})
.sheet(isPresented: $isOpen, content: {
Text("Sheet content")
})
}
}
To reproduce the issue follow those steps:
Tap just below to the top border of blue button Open sheet: the sheet opens as expected.
When the sheet is open, close the app (go back to Springboard, cmd+shift+H on iOS Simulator).
Reopen the app. You're still on the sheet view.
Close the sheet. You're back on main view with blue button. Here is the bug:
Tap again on the top of blue button, right below the top border. Nothing happens. You have to click few pixels below. There is an offset that makes all tappable items on main view not aligned.
Does anyone have seen this bug also? Is there something I do wrong?
Other notices:
When closing the app from main view, the bug doesn't appear. And even when the bug is here and I close the app from main view and reopen, the bug disappears.
If I use a .fullScreenCover instead of .sheet, the bug doesn't appear.
It really looks like a bug with .sheets open.
EDIT:
I have tried two workarounds but both don't work:
Embed the Button in an external View.
Replace Button with only the Text and add .onTapGesture{ ... } modifier to toggle isOpen #State property.
EDIT 2:
After hours of tries I could find something interesting: if, in the sheet content, I add a button to dismiss the sheet, the bug doesn't appear anymore. But if I dismiss the sheet with finger (drag from top to bottom), it still appears.
Here is modified code:
struct ContentView: View {
#State private var isOpen = false
var body: some View {
Button(action: {
isOpen.toggle()
}, label: {
Text("Open sheet")
.foregroundColor(.white)
.padding()
.background(.blue)
})
.sheet(isPresented: $isOpen, content: {
SheetContent()
})
}
}
struct SheetContent: View {
#Environment(\.dismiss) var dismiss
var body: some View {
Button(action: { dismiss() }, label: {
Text("Dismiss sheet")
})
}
}
It looks like there is something with calling (or not) the #Environment(\.dismiss) var dismiss.
The current state is a bit better as few days ago as the bug only appears when user dismiss the sheet by dragging down. But there is still something wrong.
Is there a way to programmatically call dismiss() when sheet is closed by dragging down?

SwiftUI how to force a divider for navigation bar in NavigationView

My app has simple navigation logic using navigation view. I use the inline style navigation bar:
mainView
.navigationBarTitleDisplayMode(.inline)
I notice that the navigation bar's divider is missing for the root view. And it appears when I scroll up the content a bit.
This first screenshot shows the initial state (without nav bar divider):
This second screenshot shows the state when I scroll up the content a bit, and it shows nav bar divider:
Is it possible to always show the divider without scrolling?
I think you can work around by adding a manual Divider and ScrollView under VStack so that the divider will appear beneath the navigation bar
//
// testUI.swift
// DDStore (iOS)
//
// Created by belal medhat on 19/02/2022.
//
import SwiftUI
struct navTitleBar: View {
var body: some View {
NavigationView {
// main navigationView
VStack(){
// vstack to add the divider and under it the scrollview
Divider()
ScrollView(){
Text("Hello, World!")
}.navigationBarTitleDisplayMode(.inline).navigationTitle("Title")
}
}
}
}
struct testUI_Previews: PreviewProvider {
static var previews: some View {
navTitleBar()
}
}

SwiftUI TextEditor View content is hidden behind Navigation Bar

I'm working on a SwiftUI View with a NavigationBar. The view is very simple, it's a full-page TextEditor:
struct NotesEditingScreen: View {
#State var text: String
var body: some View {
TextEditor(text: $text)
.padding(.horizontal)
.navigationBarTitle("Editing")
}
}
The issue I'm seeing, is that when landing on this screen (via a NavigationLink) the top of the TextEditor is covered up by Navigation Bar:
My desired behavior is that the TextEditor content appears beneath the Navigation Bar, like it appears after you manually scroll to the top to reveal the text.
Is there a solution/workaround to this issue? I was hoping for either some offset, a setting on NavigationBar, or some programmatic scroll behavior that could be done onAppear. Any suggestions welcome.
I think it's an unexpected behavior.
You can try this:
GeometryReader { geo in
ScrollView {
TextEditor(text: $text)
.frame(height: geo.size.height)
}
}

SwiftUI MultiviewResponder mystery crash

I came across this crash I can't explain, even after I boiling it down to the minimal components which would still cause the crash:
Repeatedly tapping the screen quickly (use two fingers) to hide/show an overlayed button with an explicit animation will crash the app under this VERY specific layout:
import SwiftUI
struct ContentView: View {
#State var showControls: Bool = true
var body: some View {
ZStack {
Rectangle().foregroundColor(Color.gray) // Removing this would fix it
VStack {
Text("someText")
Text("anotherText") // Removing this would also fix it
}
if showControls {
Text("Button")
.zIndex(1) // Removing this would also fix it
}
}
.onTapGesture {
withAnimation(.easeInOut(duration: 1)) { // Removing this would also fix it
showControls.toggle()
}
}
}
}
As mentioned in the code comments, any of these changes prevent the crash:
Remove the Rectangle from the bottom of the ZStack
Remove one of the two views from the VStack
Remove the zIndex (this will break the fade-out animation)
Remove the explicit animation call
Here is my console output:
I would like to know what's happening here, what's causing the crash ?

SwiftUI show/hide title issues with NavigationBar

I have the following code construct which gives me a lot of trouble:
//Main View
struct ContentView: View {
var body: some View {
NavigationView{
ZStack(alignment: .center){
CarouselBuilder()
ProfileInvoke().navigationBarTitle("").navigationBarHidden(true)
}
}
}
}
//Carousel filled with Cards from a DB
...code irrelevant for my problem
//Profile Invoke -> Invokes a slide out menu called Menu that has NavigationLinks in it
struct Menu: View {
var body: some View {
ZStack{
VStack(alignment: .center){
MenuButton(buttonText: "Settings", buttonCallView: AnyView(SettingsView() ))
MenuButton(buttonText: "My Favourites", buttonCallView: AnyView(MyFavouritesView()))
MenuButton(buttonText: "Sign Out", buttonCallView: AnyView(SignOutView()))
}.frame(width: UIScreen.main.bounds.width/1.2,alignment: .top)
}
}
}
//MenuButtons are basic NavigationLinks linking to certain Views given as argument when calling them
I now do wrap the ZStack in the Main View in a NavigationView which I need to to in order for the NavigationLinks to work. I also have to do this on this "top" level as I need the new View that will be invoked by the links in the slide out menu to take the entire screen and not only the width the slide out view is being displayed.
My issue is now that I certainly do not want the navigation bar to take up space in the main view. For this I set the hidden attribute for it to true. This tho, carries through the entire app and also disables the navigation view in the subviews linked to by the buttons in the menu. Which gives me no way of going back.
My question would be:
1) Is there a more elegant way of doing all of this?
2) How can I re-invoke the navigation bar in sub views? (Setting the hidden navigation bar attribute on them back to false did not work.
Below is a possible approach to hide navigation bar in root view and show in child subviews. The only needed modifications is in root view.
Tested with Xcode 11.4 / iOS 13.4
Here is a root only, child sub-views are regular and do not require special code for this case. See important notes inline.
struct RootNavigationView: View {
#State private var hideBar = true // << track hide state, and default
var body: some View {
NavigationView {
VStack {
Text("I'm ROOT")
Divider()
NavigationLink("Goto Child", destination: NextChildView(index: 1))
.simultaneousGesture(TapGesture().onEnded {
self.hideBar = false // << show, here to be smooth !!
})
}
.navigationBarHidden(hideBar)
// .navigationBarTitle("Back to Root") // << optional
.onAppear {
self.hideBar = true // << hide on back
}
}
}
}

Resources