EmptyView is not showing in iOS 15, Xcode 13 - ios

I have serious problem. My Xcode version is 13, iOS version is 15.
import SwiftUI
struct ContentView: View {
#State var isGo: Bool = false
var body: some View {
ZStack {
Button(action: {
self.isGo = true
}, label: {
Text("Go EmptyView")
})
EmptyView()
.background(Color.green)
.frame(width: 100, height: 100)
.sheet(isPresented: $isGo, onDismiss: nil, content: {
PopupView()
})
}
}
}
struct PopupView: View {
var body: some View {
Rectangle()
.fill(Color.green)
}
}
Above code is not working. But, Previous Xcode version or Previous iOS version is that code is working. Is that iOS bug? Is there anything solution?

You're very close, not sure why nobody has offered help. Your code does work but in theory isn't correct, you needed to move the where you called .sheet to outside your ZStack and it works. But here's a better approach without all the useless code.
struct ContentView: View {
#State var showemptyview: Bool = false
var body: some View {
Button("Go EmptyView") {
showemptyview.toggle()
}
.sheet(isPresented: $showemptyview) {
EmptyView()
.background(Color.green)
}
}
}

Finally in a last version you can use several sheets directly for main view and it works. You don't need to create separate EmptyView() with sheet
YourMainView()
.sheet(item: $viewModel) { item in
// some logic here
}
.sheet(isPresented: $onther_viewModel.showView, content: {
SomeAnotherView(viewModel: viewModel.getVM())
})
.sheet(isPresented: $onther_viewModel2.showView, content: {
SomeView(viewModel: viewModel.getVM())
})

Replace "EmptyView()" with "Spacer().frame(height:0)" did work for me on iOS15 and iOS14

Related

SwiftUI StackNavigationViewStyle will not update NavigationLink selection state

I recognized that my iOS app does have a double column navigation view style resp. split view for larger iPhones, like the iPhone 11 Pro Max, compared to a single column navigation.
I tried to get rid of this unwanted split view according to SwiftUI: unwanted split view on iPad by applying the .navigationViewStyle(StackNavigationViewStyle()) modifier to the NavigationView.
However, that introduces a new issue, where SwiftUI does not update the NavigationLink selection state after returning from a detail view. After coming back, the link is still shown to be active. After removing the .navigationViewStyle(StackNavigationViewStyle()) again, the selection state is updated correctly, so I think I am missing something.
I created a minimal reproducible example project. Please see the code below.
This image demonstrates the issue of a non-updating NavigationLink selection state after returning from a detail view when using the .navigationViewStyle(StackNavigationViewStyle()) modifier.
Minimal reproducible example project:
import SwiftUI
#main
struct TestSwiftUIApp: App {
var body: some Scene {
WindowGroup {
View1()
}
}
}
struct View1: View {
var body: some View {
NavigationView {
List {
NavigationLink(
destination: View2(),
label: {
Text("View2")
}
).isDetailLink(false)
NavigationLink(
destination: View2(),
label: {
Text("View2")
}
).isDetailLink(false)
}.listStyle(InsetGroupedListStyle())
.navigationTitle("View1")
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
struct View2: View {
#State var presentView3: Bool = false
var body: some View {
List {
Text("Foo")
NavigationLink("View3",
destination: View3(presentView3: $presentView3),
isActive: $presentView3
).isDetailLink(false)
Text("Bar")
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("View 2")
}
}
struct View3: View {
#Binding var presentView3: Bool
#State
var isAddViewPresented: Bool = false
var body: some View {
List {
Button(action: {presentView3 = false}, label: {
Text("Dismiss")
})
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("View3")
.toolbar {
ToolbarItem {
Button(action: {isAddViewPresented.toggle()}, label: {
Label("Add", systemImage: "plus.circle.fill")
})
}
}
.sheet(isPresented: $isAddViewPresented, content: {
Text("DestinationDummyView")
})
}
}

Create a view over navigationView, SwiftUI

I'm creating a view embedded in a ZStack, something like this:
ZStack(alignment: .top) {
content
if self.show {
VStack {
HStack {
This is a viewModifier so I call this in my main view with for example: .showView().
But what happened is that if I have a NavigationView, this view is only showing below the navigationView. (I have a navigationViewTitle that is over my view).
How can I solve this problem? I was thinking about some zIndex but it is not working. I thought also about some better placement of this .showView(), but nothing to do.
Here is a demo of possible approach (it can be added animations/transitions, but it is out of topic). Demo prepared & tested with Xcode 11.4 / iOS 13.4
struct ShowViewModifier<Cover: View>: ViewModifier {
let show: Bool
let cover: () -> Cover
func body(content: Content) -> some View {
ZStack(alignment: .top) {
content
if self.show {
cover()
}
}
}
}
struct DemoView: View {
#State private var isPresented = false
var body: some View {
NavigationView {
VStack {
NavigationLink("Link", destination: Button("Details")
{ self.isPresented.toggle() })
Text("Some content")
.navigationBarTitle("Demo")
Button("Toggle") { self.isPresented.toggle() }
}
}
.modifier(ShowViewModifier(show: isPresented) {
Rectangle().fill(Color.red)
.frame(height: 200)
})
}
}

SwiftUI modal presentation works only once from navigationBarItems

Here is a bug in SwiftUI when you show modal from button inside navigation bar items.
In code below Button 1 works as expected, but Button 2 works only once:
struct DetailView: View {
#Binding var isPresented: Bool
#Environment (\.presentationMode) var presentationMode
var body: some View {
NavigationView {
Text("OK")
.navigationBarTitle("Details")
.navigationBarItems(trailing: Button(action: {
self.isPresented = false
// or:
// self.presentationMode.wrappedValue.dismiss()
}) {
Text("Done").bold()
})
}
}
}
struct ContentView: View {
#State var showSheetView = false
var body: some View {
NavigationView {
Group {
Text("Master")
Button(action: { self.showSheetView.toggle() }) {
Text("Button 1")
}
}
.navigationBarTitle("Main")
.navigationBarItems(trailing: Button(action: {
self.showSheetView.toggle()
}) {
Text("Button 2").bold()
})
}.sheet(isPresented: $showSheetView) {
DetailView(isPresented: self.$showSheetView)
}
}
}
This bug is from the middle of the last year, and it still in Xcode 11.3.1 + iOS 13.3 Simulator and iOS 13.3.1 iPhone XS.
Is here any workaround to make button work?
EDIT:
Seems to be tap area goes somewhere down and it's possible to tap below button to show modal.
Temporary solution to this is to use inline navigation bar mode:
.navigationBarTitle("Main", displayMode: .inline)
Well, the issue is in bad layout (seems broken constrains) of navigation bar button after sheet has closed
It is clearly visible in view hierarchy debug:
Here is a fix (workaround of course, but safe, because even after issue be fixed it will continue working). The idea is not to fight with broken layout but just create another button, so layout engine itself remove old-bad button and add new one refreshing layout. The instrument for this is pretty known - use .id()
So modified code:
struct ContentView: View {
#State var showSheetView = false
#State private var navigationButtonID = UUID()
var body: some View {
NavigationView {
Group {
Text("Master")
Button(action: { self.showSheetView.toggle() }) {
Text("Button 1")
}
}
.navigationBarTitle("Main")
.navigationBarItems(trailing: Button(action: {
self.showSheetView.toggle()
}) {
Text("Button 2").bold() // recommend .padding(.vertical) here
}
.id(self.navigationButtonID)) // force new instance creation
}
.sheet(isPresented: $showSheetView) {
DetailView(isPresented: self.$showSheetView)
.onDisappear {
// update button id after sheet got closed
self.navigationButtonID = UUID()
}
}
}
}

Problems with layout of some rows in SwiftUI list

I have tried to find a similar problem asked before, but failed.
I have a simple view with list. I am using a ForEach to show 10 iterations of the some list item to create a layout before I will add real data to this list. I have a problem with last 2 rows not rendering correctly. But sometimes it’s other row. I have tested on an iPhone too and sometimes it’s one row, sometimes another. The code for the view with list is this:
import SwiftUI
struct LocksView: View {
#State private var locksPaid = 0
var body: some View {
NavigationView {
List {
DateView()
.listRowInsets(EdgeInsets())
Picker(selection: $locksPaid, label: Text("Picker")) {
Text("All").tag(0)
Text("Not paid (2)").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.padding(10)
ForEach(0 ..< 10) {item in
LocksItemView()
}
}
.navigationBarTitle(Text("Locks"))
.navigationBarItems(trailing: EditButton())
}
}
}
The code for list items is this:
import SwiftUI
struct LocksItemView: View {
#State private var paid : Bool = false
var body: some View {
HStack {
Text("L15")
.font(.title)
.fontWeight(.heavy)
.multilineTextAlignment(.center)
.frame(width: 80)
VStack(alignment: .leading) {
Text("nickname")
.fontWeight(.bold)
Text("category")
Text("4 000 THB")
.fontWeight(.bold)
}
Spacer()
Toggle(isOn: self.$paid) {
Text("Paid")
}
.labelsHidden()
}
}
}
Why is toggle broken in some rows on my list? Why it moves to the left side?
It is not last items. If you set in your ForEach 20 instead of 10 and scroll up & down you'll see much more interesting issue.
I assume the reason, actually List bug, is the same as in this topic.
Workaround If it is not critical to you then use ScrollView instead of List, as tested there is no bug for it. Otherwise, file a Radar and wait for fix.
I tried your code at simulators first and had same issue too. But then I remembered, that there are some problems with 13.2 iOS and tried to run it on my device (iPhone 7, iOS 13.1.1) and everything works fine! I think that is the problem in 13.2 iOS, not in the List. There is sample, how I changed code for demonstration that everything is ok:
import SwiftUI
struct LocksView: View {
#State private var locksPaid = 0
var body: some View {
NavigationView {
List {
Picker(selection: $locksPaid, label: Text("Picker")) {
Text("All").tag(0)
Text("Not paid (2)").tag(1)
}
.pickerStyle(SegmentedPickerStyle())
.padding(10)
ForEach(0 ..< 200) {item in
LocksItemView(number: item)
}
}
.navigationBarTitle(Text("Locks"))
.navigationBarItems(trailing: EditButton())
}
}
}
struct LocksItemView: View {
#State private var paid : Bool = false
var number: Int
var body: some View {
HStack {
Text("L\(self.number)")
.font(.title)
.fontWeight(.heavy)
.multilineTextAlignment(.center)
.frame(width: 80)
VStack(alignment: .leading) {
Text("nickname")
.fontWeight(.bold)
Text("category")
Text("4 000 THB")
.fontWeight(.bold)
}
Spacer()
Toggle(isOn: self.$paid) {
Text("Paid")
}
.labelsHidden()
}
}
}
and on my phone the result is:
so there are bugs in 13.2 version and I hope Apple will fix them all

Push, Segue, Summon, Navigate to View Programmatically SwiftUI

I'm trying to do the simplest of things. I just want to summon a new SwiftUI view programmatically - not with a button, but with code. I've read a couple of dozens posts and Apple docs on this - but almost all that I've found relates to code that has been renamed or deprecated. The closest I have found is:
NavigationLink(destination: NewView(), isActive: $something) {
EmptyView()
}
But this does not work for me in Xcode Beta 7. Here's the trivial app:
struct ContentView: View {
#State private var show = false
var body: some View {
VStack {
Text("This is the ContentView")
Toggle(isOn: $show) {
Text("Toggle var show")
}
.padding()
Button(action: {
self.show = !self.show
}, label: {
Text(self.show ? "Off" : "On")
})
Text(String(show))
//this does not work - the ContentView is still shown
NavigationLink(destination: SecondView(), isActive: $show)
{
EmptyView()
}
//this does not work - it adds SecondView to ContentView
//I want a new view here, not an addition
//to the ContentView()
// if show {
// //I want a new view here, not an addition to the ContentView()
// SecondView()
// }
}
}
}
And the brutally simple destination:
struct SecondView: View {
var body: some View {
Text("this is the second view!")
}
}
I must be missing something extremely simple. Any guidance would be appreciated.
iOS 13.1, Catalina 19A546d, Xcode 11M392r
A couple of things. First, NavigationLink must be imbedded in a NavigationView to work. Second, the link doesn't need a view as you showed it. This should show the second view. I will leave to you to update the other elements.
var body: some View {
NavigationView{
VStack {
Text("This is the ContentView")
Toggle(isOn: $show) {
Text("Toggle var show")
}
.padding()
Button(action: {
self.show = !self.show
}, label: {
Text(self.show ? "Off" : "On")
})
Text(String(show))
//this does not work - the ContentView is still shown
NavigationLink(destination: SecondView()){
Text("Click to View")}
Spacer()
// {
// EmptyView()
// }
//this does not work - it adds SecondView to ContentView
//I want a new view here, not an addition
//to the ContentView()
// if show {
// //I want a new view here, not an addition to the ContentView()
// SecondView()
// }
}
}
}

Resources