SwiftUI weird animation when showing popover on iPhone - swiftui

If I show a popover in SwiftUI on the iPhone, and dismiss by swiping down the popover, the dismiss animation is incorrect.
This happens with Xcode 12.0.1, on iOS 14.0.1.
struct MyPopover: View {
var body: some View {
VStack {
Spacer()
Button("Show alert") {
// blah
}
Spacer()
}
.frame(maxWidth: .infinity)
.background(Color.green.edgesIgnoringSafeArea(.all))
}
}
struct ContentView: View {
#State private var isShowingPopover = false
var body: some View {
Button("Tap me to show a popover!") {
self.isShowingPopover.toggle()
}
.popover(isPresented: self.$isShowingPopover) {
MyPopover()
}
}
}
This is the result:
The "fix" is to use .sheet() on iPhone, but is there a reason why this behavior exists?

Related

SwiftUI: Touches not working after returning from background

Got a strange bug/error. Touches stops working at the top after closing and open the app.
To reproduce:
Click the blue bar to trigger "onTapGesture"
Swipe up to go back to springboard
Open the app
Drag down to close the modal
Click the blue bar (Will not work)
Interesting, if I remove the "Color.red.ignoresSafeArea()" It works as expected. In iOS 15, it also works as expected.
Is this a bug in SwiftUI?
Any suggestion for a workaround?
public struct TestView: View {
#State private var showModal = false
public var body: some View {
ZStack {
Color.red.ignoresSafeArea()
VStack(spacing: 0) {
Color.blue
.frame(height: 20)
.onTapGesture {
showModal = true
}
Color.white
}
}
.sheet(isPresented: $showModal, content: {
Text("HELLO")
})
}
}
I see the same happening on iPhone 14 Pro, iOS 16.2, Xcode 14.2
A workaround could be to dismiss the sheet when the app goes into the background:
struct TestView: View {
#State private var showModal = false
#Environment(\.scenePhase) var scenePhase
public var body: some View {
ZStack {
Color.red.ignoresSafeArea()
VStack(spacing: 0) {
Color.blue
.frame(height: 20)
.onTapGesture {
showModal = true
}
Color.white
}
}
.sheet(isPresented: $showModal, content: {
Text("HELLO")
})
.onChange(of: scenePhase) { scenePhase in
if scenePhase == .background {
showModal = false
}
}
}
}

SwiftUI how to align the navigation bar title on center when user taps on a navigation link?

I have a TabView in a struct. I add a NavigationView and a NavigationLink. When the view shows up the navigation bar is centered. When i tap on the navigation link the text is on the leading side. How can i make the navigation bar text align to center after i tap the navigation link in iOS 16 and above? I have searched stackoverflow and nothing seems to work in iOS 16 with this specific implementation i am posting below. Any help appreciated.
import Foundation
import SwiftUI
public struct TabViewTest: View {
#State var selectedTab: Int = 0
public var body: some View {
TabView (selection: $selectedTab) {
NavigationView {
NavigationLink(destination: Text("destination"))
{
FeedView(selectedTab: $selectedTab)
.navigationBarTitle("Feed", displayMode: .inline)
.toolbar {
ToolbarItemGroup {
Image(systemName :"magnifyingglass.circle")
.onTapGesture {
selectedTab = 1
}
}
}
}
}
.tabItem {
Text("Feed")
.foregroundColor(.white)
}
.tag(0)
}
}
struct ProfileView: View {
#Binding var selectedTab: Int
var body: some View {
ZStack {
Color.white.ignoresSafeArea()
VStack {
Text("Nav link test tap anywhere")
.font(.largeTitle)
.foregroundColor(.white)
}
}
}
}

SwiftUI: Logout and switch screens from a popover

In my app, I have a View which is set to either a login View or a home TabView, depending on if the user is logged in. From the TabView, the user can go to a profile popover and logout. I want to switch back to the login View from this popover.
I tried dismissing the popover and immediately logging the user out, but when I test on a real device, what happens is the popover stays on the screen and also no longer responds to user input. It can't be dismissed. I'm not sure why, and what should I do instead?
Starting View:
struct StartView: View {
#EnvironmentObject var authService:AuthService
var body: some View {
ZStack {
if(!authService.signedIn) {
LoginView()
} else {
HomeView()
}
}
}
}
Home TabView:
import SwiftUI
struct HomeView: View {
#State private var showingProfilePopover:Bool = false
var body: some View {
TabView {
NavigationView {
VStack(alignment: .leading) {
Text("Tab 1")
.padding(.leading, 30)
}
.toolbar {
ToolbarItem {
Button(action: {
showingProfilePopover = true
}, label: {
Image(systemName: "person.crop.circle").imageScale(.large)
}
)
}
}
}.popover(isPresented: $showingProfilePopover) {
ProfileView(isPresented: $showingProfilePopover)
}
.tabItem {
Image(systemName: "list.bullet")
.font(.system(size: 26))
Text("Tab 1")
}
NavigationView {
VStack(alignment: .leading) {
Text("Tab 2")
}
}.tabItem {
Image(systemName: "books.vertical")
.font(.system(size: 26))
Text("Tab 2")
}
}
}
}
Popover:
struct ProfileView: View {
#EnvironmentObject var authService:AuthService
#Binding var isPresented: Bool
var body: some View {
Button("Logout") {
// Close the popup and switch to LoginView
print("Tapped logout")
isPresented = false
authService.signOut()
}
.font(Font.custom("OpenSans-Regular", size: 18))
.padding(20)
}
}
LoginView:
import SwiftUI
struct LoginView: View {
#EnvironmentObject var authService:AuthService
var body: some View {
VStack {
Button("Login") {
self.authService.signIn()
}.buttonStyle(.borderedProminent)
}
}
}
AuthService:
import SwiftUI
class AuthService: ObservableObject {
#Published var signedIn:Bool
init(signedIn:Bool) {
self.signedIn = signedIn
}
func signIn() {
self.signedIn = true
}
func signOut(){
self.signedIn = false
}
}
Seems like an issue connected to .popover. I can reproduce the issue, but it works just fine using .sheet instead.
Consider attaching the .popover on the TabView or the Button itself then it seems to work just fine.
I realized the issue only happens on the older versions of iOS. it works fine on iOS 15, but not on iOS 14 and below iOS 14.
#main
struct LoginApp: App {
let authService: AuthService
init() {
authService = AuthService(signedIn: false)
}
var body: some Scene {
WindowGroup {
StartView().environmentObject(authService)
}
}
}
This is iPhone 8, iOS 14

SwiftUI on iPadOS - Multitasking does not show the correct navigation title

This is on iPadOS 15.4. I'm using Xcode 13.3.1. I'm using SwiftUI. I have two files:
iPadNavigationViewTestApp.swift
#main
struct iPadNavigationViewTestApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
List {
NavigationLink(destination: ContentView()) {
Text("Content View 1")
}
NavigationLink(destination: AnotherContentView()) {
Text("Content View 2")
}
NavigationLink(destination: YetAnotherContentView()) {
Text("Content View 3")
}
}
.listStyle(.sidebar)
.navigationTitle("This is a test")
ContentView()
}
}
}
}
ContentView.swift
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
.navigationTitle("ContentView")
}
}
struct AnotherContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
.navigationTitle("AnotherContentView")
}
}
struct YetAnotherContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
.navigationTitle("YetAnotherContentView")
}
}
In the multitasking interface of iPad, the title of the view was not shown correctly:
In the simulator I switched to the second view using the sidebar, and the detail view's navigation title changed to "AnotherContentView". Yet the view title on multitasking is still "ContentView". How do I let it show the correct title? Is this a bug of SwiftUI?

SwiftUI: NavigationLink pops out immediately on WatchOS 8.1RC in Tabview

I have discovered a regression in watchOS 8.1RC with NavigationLink triggered from a TabView.
It's immediately dismissed.
It was working in watchOS 8.0 or in Simulator (watchOS 8.0).
Do you know a workaround ?
Thanks
Sample code:
import SwiftUI
#main
struct TestNavigationApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
}
}
}
}
struct ContentView: View {
var body: some View {
List {
NavigationLink(destination: ContentView1()) {
Text("To TabView")
}
}
}
}
struct ContentView1: View {
var body: some View {
TabView {
NavigationView {
NavigationLink(destination: ContentView2()) {
Text("To ContentView2")
}
}
VStack {
Text("Screen2")
}
}
}
}
struct ContentView2: View {
var body: some View {
Text("ContentView2")
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I'm experiencing the same issue with watchOS 8.1 (and 8.3 beta) while it was working with previous watchOS versions.
We were able to get it working again by moving the NavigationView inside the TabView. This workaround isn't ideal at all but it does seem to work.
#State private var tabSelection = 1
var body: some Scene {
WindowGroup {
TabView(selection: $tabSelection) {
NavigationView {
// List goes here
}
.tag(1)
VStack(alignment: .center, spacing: 12, content: {
// content 2nd tab: we didn't have a list in the 2nd tab
})
.tag(2)
}
}
}
However, there are 2 things impacted with this fix:
I didn't get the navigationBarTitle working, so there won't be a title on top of the screen.
If you click on an item in the list, it will navigate to your page (as expected) but the TabView dots at the bottom of the screen will remain.