SwiftUI Constraint Issue For NavigationTitle in iOS 15 - swiftui

I'm having an issue with Xcode throwing a constraint message in the console every time I use a NavigationTitle.
I'd like to say that I've read through the following post and have tried adding StackedNavigationViewStyle(). This worked for iOS 14 and Xcode 12.3. Since upgrading to iOS 15 and Xcode 13 the solution stops working.
SwiftUI NavigationView navigationBarTitle LayoutConstraints issue
The message is.
[LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one
you don't want. Try this: (1) look at each constraint and try to
figure out which you don't expect; (2) find the code that added the
unwanted constraint or constraints and fix it. (
"<NSLayoutConstraint:0x28189d540 UIView:0x155f53c50.trailing == _UIBackButtonMaskView:0x155f538f0.trailing (active)>",
"<NSLayoutConstraint:0x281890af0 'Mask_Trailing_Trailing' _UIBackButtonMaskView:0x155f538f0.trailing == _UIButtonBarButton:0x155f52d00.trailing (active)>",
"<NSLayoutConstraint:0x2818912c0 'MaskEV_Leading_BIB_Trailing' H:[_UIModernBarButton:0x155f53610]-(0)-[UIView:0x155f53c50]
(active)>",
"<NSLayoutConstraint:0x281890c30 'UINav_static_button_horiz_position'
_UIModernBarButton:0x155f53610.leading == UILayoutGuide:0x2802a5ce0'UIViewLayoutMarginsGuide'.leading
(active)>",
"<NSLayoutConstraint:0x281890640 'UINavItemContentGuide-leading' H:[_UIButtonBarButton:0x155f52d00]-(6)-[UILayoutGuide:0x2802a5dc0'UINavigationBarItemContentLayoutGuide']
(active)>",
"<NSLayoutConstraint:0x28189dd60 'UINavItemContentGuide-trailing' UILayoutGuide:0x2802a5dc0'UINavigationBarItemContentLayoutGuide'.trailing
== _UINavigationBarContentView:0x155f51f30.trailing (active)>",
"<NSLayoutConstraint:0x28189b340 'UIView-Encapsulated-Layout-Width'
_UINavigationBarContentView:0x155f51f30.width == 0 (active)>",
"<NSLayoutConstraint:0x28189e300 'UIView-leftMargin-guide-constraint'
H:|-(8)-UILayoutGuide:0x2802a5ce0'UIViewLayoutMarginsGuide'
(active, names: '|':_UINavigationBarContentView:0x155f51f30 )>" )
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x28189d540 UIView:0x155f53c50.trailing ==
_UIBackButtonMaskView:0x155f538f0.trailing (active)>
Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints
to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView
listed in <UIKitCore/UIView.h> may also be helpful.
When adding StackedNavigationViewStyle to an iOS 15 app the Title and TabBar become inactive and the list moves underneath.
The code for this is below.
struct Tab1: View {
var body: some View {
// // Used for PacificBlue color.
UITableView.appearance().backgroundColor = .clear
// UITableView.appearance().separatorColor = UIColor(Color.white)
return NavigationView {
ZStack {
Color.pacificBlue
.edgesIgnoringSafeArea(.all)
List {
ForEach(1..<100) { index in
if #available(iOS 15.0, *) {
NavigationLink(destination: DetailView()) {
HStack {
Text("Row \(index)")
//
.listRowSeparatorTint(Color.pink)
}
.background(Color.pacificBlue)
} // NavigationLink
.listRowSeparatorTint(Color.pink)
} else {
// Fallback on earlier versions
NavigationLink(destination: DetailView()) {
Text("Row \(index)")
} // NavigationLink
} // if else
} // ForEach
.listRowBackground(Color.pacificBlue)
} // List
.listStyle(PlainListStyle())
} // ZStack
.navigationTitle("Title")
} // NavigationView
.navigationViewStyle(StackNavigationViewStyle())
} // View
}
If built on iOS 14 the expected behavior is seen.
Any help would be appreciated.

Recently, I was challenged by the same issue of NavigationBar and TabBar becoming transparent and unresponsive to scroll with IOS 15.0 exclusively, while IOS 14 and IOS 15.2 and above had normal behavior. After many hours of identifying the problem, I discovered that it caused by combination of .navigationViewStyle(.stack) and the ZStack that's used inside to provide Color as background. I have no explanation why it behaves like that, neither why the solutions work.
Option 1: Wrap the NavigationView with another ZStack
struct ContentView: View {
var body: some View {
UITableView.appearance().backgroundColor = .clear
return ZStack {
NavigationView {
ZStack {
Color.blue
.edgesIgnoringSafeArea(.all)
List {
ForEach(1...50, id:\.self) { index in
Text("Row \(index)")
}
}
}
.navigationTitle("Title")
}
.navigationViewStyle(.stack)
}
}
}
Option 2: Apply the ZStack only to IOS 15 with a ViewModifier
struct ContentView: View {
var body: some View {
UITableView.appearance().backgroundColor = .clear
return NavigationView {
ZStack {
Color.blue
.edgesIgnoringSafeArea(.all)
List {
ForEach(1...50, id:\.self) { index in
Text("Row \(index)")
}
}
}
.navigationTitle("Title")
}
.navigationViewStyle(.stack)
.modifier(NavigationModifier())
}
}
struct NavigationModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 15.0, *) {
ZStack {
content
}
} else {
content
}
}
}

I had the same problem using NavigationView and TabView together. Until iOS 14 I used the code bellow and it worked without problem:
NavigationView{
TabView(selection: $selection) {
...
}
}
However, since iOS 15, the same code started having the same navigationTitle you're having.
After a lot of time searching for a solution I found that if we change the order it works perfectly:
struct ContentView: View {
var body: some View {
TabView(selection: $selection) {
NavigationView {
Text("Example 1")
.navigationTitle("One")
}
.tabItem {
Text("One")
}
.tag("One")
NavigationView {
Text("Example 2")
.navigationTitle("Two")
}
.tabItem {
Text("Two")
}
.tag("Two")
}
}
}

Related

SwiftUI: NavigationView bug in WatchOS 8.1 When List and ForEach is embedded in TabView

The code below worked fine in WatchOS 7 and 8.0, but now in 8.1 tapping on the row will navigate to the destination but then immediately navigates back to the root view.
I filed Feedback #FB9727188 and included the below to demonstrate the issue.
struct ContentView: View {
#State var tabIndex:Int = 0
var body: some View {
TabView(selection: $tabIndex) {
ListView()
.tabItem { Group{
Text("List")
}}.tag(0)
.padding(.bottom, 1.0)
Text("Second View")
.tabItem { Group{
Text("Second")
}}.tag(1)
.padding(.bottom, 1.0)
Text("Third View")
.tabItem { Group{
Text("ThirdView")
}}.tag(2)
}
}
}
struct ListView: View {
var body: some View {
List {
ForEach((1...10).reversed(), id: \.self) {_ in
NavigationLink(destination: Text("test")) {
Text("Tap Me but we'll just be back here")
}
}
}
}
}
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 {
// 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.

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.

NavigationLink in ContextMenu no longer working in iOS14 xcode12 beta3?

Wondering if anyone else has this issue, and if a workaround has been found. This works fine in iOS 13, but seems broken in iOS 14.
I am just trying to fire off a NavigationLink to another View, from a .contextMenu.
My code is as below.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
HStack {
Text("I am a text in a HStack ")
}
HStack {
NavigationLink(destination: TestView()) {
VStack {
Image(systemName:"gauge")
.font(.system(size: 31))
}
}
}
}
.contextMenu {
NavigationLink(destination: TestView()) {
Text("Navigate to TestView")
Image(systemName: "pencil")
}
}
}
}
}
The Destination TestView() is just a boilerplate "Hello World" view.
If I click not the Icon associated in the Stack, this triggers the navigation perfectly. But the same link in the context menu does not.
When I select it in the contextmenu, nothing happens. I.e I can select the menu item, but all it does is close the context menu and I stay on the same view.
Anyone else found this? solved it ?
Thanks
Here is a demo of possible approach. Tested with Xcode 12b3 / iOS 14 (also valid for SwiftUI 1.0)
struct ContentView: View {
#State private var showLink = false
var body: some View {
NavigationView {
VStack {
HStack {
Text("I am a text in a HStack ")
}
HStack {
NavigationLink(destination: Text("TestView")) {
VStack {
Image(systemName:"gauge")
.font(.system(size: 31))
}
}
}
}
.background(NavigationLink("", destination: Text("TestView"), isActive: $showLink))
.contextMenu {
Button(action: { self.showLink = true }) {
HStack {
Text("Navigate to TestView")
Image(systemName: "pencil")
}
}
}
}
}
}

SwiftUI Navigation Controller stuttering with two Navigationlinks per List row

I am trying to create two NavigationLinks in a repeating List. Each has a separate destination. The code all works fine until I imbed the call to the root view in a List/ForEach loop. At which point the navigation becomes very strange.
Try to click on either link and then click the back indicator at the top. It will go to one NavigationLink, and then the other. Sometimes in a different order, and sometimes it will auto-return from one of the links, and othertimes it won't open the second detail view until you return from the first detail view. It does this both in Preview, as well as if you build and run the application.
I have distilled down the code to the most basic below. If you comment the 2 lines as indicated in ContentView, you will then see correct behavior.
I am running Catalina 10.15.5, xCode 11.6, with the application target of IOS 13.6.
How can I modify the code, so that it will work with the List/ForEach loop?
import SwiftUI
struct DetailView1: View {
var body: some View {
HStack {
Text("Here is Detail View 1." )}
.foregroundColor(.green)
}
}
struct DetailView2: View {
var body: some View {
HStack {
Text( "Here is Detail View 2.") }
.foregroundColor(.red)
}
}
struct RootView: View {
var body: some View {
HStack {
VStack {
NavigationLink(destination: DetailView1())
{ VStack { Image(systemName: "ant.circle").resizable()
.frame(width:75, height:75)
.scaledToFit()
}.buttonStyle(PlainButtonStyle())
Text("Tap for Detail 1.")
.foregroundColor(.green)
}
}
NavigationLink(destination: DetailView2())
{ Text("Tap for Detail 2.")
.foregroundColor(.red)
}
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
// Comment the following line for correct behavior
List { ForEach(0..<3) {_ in
RootView()
// Comment the following line for correct behavior
} }
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ContentView()
.navigationBarTitle("Strange Behavior")
}
}
}
In your case both navigation links are activated at once user tap a row, to avoid this below is possible approach
Tested with Xcode 12 / iOS 14
The idea is to have one link which is activated programmatically and destination is selected dynamically depending on which button is clicked
struct RootView: View {
#State private var isFirst = false
#State private var isActive = false
var body: some View {
HStack {
VStack {
Button(action: {
self.isFirst = true
self.isActive = true
})
{ VStack { Image(systemName: "ant.circle").resizable()
.frame(width:75, height:75)
.scaledToFit()
}
Text("Tap for Detail 1.")
.foregroundColor(.green)
}
}
Button(action: {
self.isFirst = false
self.isActive = true
})
{ Text("Tap for Detail 2.")
.foregroundColor(.red)
}
.background(
NavigationLink(destination: self.destination(), isActive: $isActive) { EmptyView() }
)
}
.buttonStyle(PlainButtonStyle())
}
#ViewBuilder
private func destination() -> some View {
if isFirst {
DetailView1()
} else {
DetailView2()
}
}
}

Corrupted Navigation Views

I'm pretty sure this is a bug in SwiftUI, but I wondered if anyone has encountered it and figured out a workaround. My normal use case is to have a search field appear, but I've simplified it to the point where a simple text string exhibits the bug.
Create a single-view app, copy this into ContentView, and run it. Tap the search icon twice, then scroll the view; you'll see the text scrolling UNDER the title.
import SwiftUI
struct ContentView: View {
private var items = (0 ... 50).map {String($0)}
#State private var condition = false
var searchButton: some View {
Button(action: {self.condition.toggle()}) {
Image(systemName: "magnifyingglass").imageScale(.large)
}
}
var body: some View {
NavigationView {
VStack {
if condition {
Text("Peekaboo")
}
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
}
}
}
}
.navigationBarTitle("List of Items")
.navigationBarItems(leading: searchButton)
}
}
}
Maybe it is a bug, submit feedback to Apple, but currently this is how NavigationView behaves - it collapses navigation bar only if its top content is List/ScrollView/Form. So to solve the issue move your VStack either into a List or out of NavigationView
1)
var body: some View {
NavigationView {
List {
if condition {
Text("Peekaboo")
}
ForEach(items, id: \.self) {item in
2)
var body: some View {
VStack {
if condition {
Text("Peekaboo")
}
NavigationView {
List {
It seems that a View cannot cope with variable number of views.
A workaround this strange behavior is this:
import SwiftUI
struct ContentView: View {
private var items = (0 ... 50).map {String($0)}
#State private var condition = false
var searchButton: some View {
Button(action: {self.condition.toggle()}) {
Image(systemName: "magnifyingglass").imageScale(.large)
}
}
var body: some View {
NavigationView {
VStack {
if condition {
Text("Peekaboo")
} else {
Text("")
}
// or use this Text(condition ? "Peekaboo" : "")
List {
ForEach(items, id: \.self) {item in
HStack {
Text(item)
}
}
}
}
.navigationBarTitle("List of Items")
.navigationBarItems(leading: searchButton)
}
}
}
Let me know if it works, if not let us know what device/system you are using. Tested with Xcode 11.6 beta, Mac 10.15.5, target ios 13.5 and mac catalyst.