TL;DR
SwiftUI's UISplitViewSplitView controller setup is grouping my table view. Why?
struct ContentView: View {
var body: some View {
NavigationView {
List(0..<5) { value in
HStack {
Text("Value \(value)")
}
}
.navigationTitle("List View")
Text("Split View")
}
}
}
Sample 1
Longer
I'm working through the SwiftUI track of WWDC20, treating it as a code-a-long. Beginning with the first video, "Introduction to SwiftUI".
I started a whole sample project with resources and everything. Everything goes swimmingly until I start working to support the iPad. I'm aware that there did end up being some changes since the videos were produced, but haven't found any relevant to the UISplitController.
After the bug presented itself I stood up a smaller project that just presented the split controller to test this issue.
So long as I'm just presenting the ContentView for iPhone, the UI works as expected.
struct ContentView: View {
var body: some View {
NavigationView {
List(0..<5) { value in
HStack {
Text("Value \(value)")
}
}
.navigationTitle("List View")
}
}
}
Sample 2
I've tried executing on device and the problem persists.
Just use plain list style explicitly:
var body: some View {
NavigationView {
List(0..<5) { value in
HStack {
Text("Value \(value)")
}
}
.listStyle(PlainListStyle()) // << here !!
.navigationTitle("List View")
Text("Split View")
}
}
Related
I am making a menu for my game using NavigationView and NavigationLink. I have three views as such:
struct MainMenuView: View {
var body: some View {
NavigationView {
// relevant stuff
NavigationLink(destination: CustomGameSettingsView()) {
Text("Custom Game")
}
}
}
}
struct CustomGameSettingsView: View {
var body: some View {
NavigationView {
// ui to change game settings and stuff
NavigationLink(destination: MyGameView(customSettings)) {
Text("Play Game!")
}
}
}
}
Note that the CustomGameSettingsView shows the navigationBarBackButton
struct MyGameView: View {
var body: some View {
myGameViews {
// stuff
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
.navigationBarTitle(Text("myGame"))
.edgesIgnoringSafeArea([.top, .bottom])
// these are the things I have tried to get rid of the navigationBackButton
}
.onAppear() {
self.navigationBarBackButtonHidden(true)
} // another thing I tried
}
When I navigate from MainMenuView to CustomGameSettingsView, I gain a navigationBarBackButton which stays with me when I navigate to MyGameView. How do I get rid of that on the final navigation? I want the back button on the settings menu but I want my game to be fullscreen. Is NavigationView the wrong tool in this instance?
Thank you in advance.
The problem here is you use another NavigationView{} inside the CustomGameSettingsView. You don't have to use another NavigationView because all your views (except MainMenuView) are already inside the NavigationView of the MainMenuView.
To fix this, replace the NavigationView inside the CustomGameSettingsView with a different container, then everything will work fine. Also, remove all your onAppear{}.
struct CustomGameSettingsView: View {
var body: some View {
VStack { //replace NavigationView with something else
NavigationLink(destination: MyGameView()) {
Text("Play Game!")
}
}
}
}
The .navigationTitle on some views seem to be having some problems. On some views (and only some of the time), the .navigationTitle will not change from .large to .inline as would be expected. Instead, the title stays in place when scrolling up, and the navigation bar is completely invisible (as outlined in the video below). This is all reproducible every time.
Video of reproducible .navigationTitle bugs
I haven't found any people on stack overflow or the Apple Developer forums who have run into this exact issue. There have some people who have produced similar results as this, but those were all fixed by removing some stylizing code to the .navigationbar, of which I am not making any modifications to it anywhere in my code.
Below are some snippets of my code:
import SwiftUI
struct WelcomeUI: View {
var body: some View {
NavigationView {
VStack {
//NavigationLink(destination: SignupUI(), label: {
//Text("Sign Up")
//}
NavigationLink(destination: LoginUI(), label: {
Text("Log In")
})
}
}
}
}
struct LoginUI: View {
var body: some View {
VStack {
NavigationLink(destination: MainUI(), label: { Text("Log In") })
//Button(action: { ... }
}
.navigationBarHidden(false)
}
}
struct MainUI: View {
#State var selectedTab: Views = .add
var body: some View {
TabView(selection: $selectedTab) {
SpendingView()
.tabItem {
Image(systemName: "bag.circle")
Text("Spending")
}.tag(Views.spending)
Text("Adding View")
.tabItem {
Image(systemName: "plus")
Text("Add")
}.tag(Views.add)
Text("Edit View")
.tabItem {
Image(systemName: "pencil")
Text("Edit")
}.tag(Views.edit)
SettingsView()
.tabItem {
Image(systemName: "gear")
Text("Settings")
}.tag(Views.settings)
}
.navigationBarTitle(Text(selectedTab.rawValue))
.navigationBarBackButtonHidden(true)
}
}
enum Views: String {
case spending = "Spending"
case add = "Add"
case edit = "Edit"
case settings = "Settings"
}
struct SettingsView: View {
var body: some View {
VStack{
ZStack {
Form {
Section(header: Text("Section Header")) {
NavigationLink(destination: WelcomeUI()) {
Text("Setting Option")
}
}
Section {
//Button("Log Out") {
//self.logout()
//}
Text("Log Out")
}
}
Button("say-high", action: {print("Hi")})
}
}
}
}
struct SpendingView: View {
var body: some View {
ScrollView{
Text("SpendingView")
NavigationLink("subSpending", destination: SubSpendingView())
}.padding()
}
}
struct SubSpendingView: View {
var body: some View {
ScrollView{
Text("SubSpendingView")
}.navigationBarTitle("SubSpending")
}
}
It almost seems like a bug in SwiftUI itself just because the fact that bringing down the control centre makes it kind of work, but with no animation (as seen in the video). Also, changing which view is selected first in #State var selectedTab: Views seems to let the view selected to work as expected, but lets the rest of the tabs mess up.
When I build and run the app on my iPad, it behaves as expected with no bugs, it's only when run on my iPhone and the iOS simulator on Mac that it does this, any way to fix this?
For this to work flawlessly the ScrollView needs to be the direct child of the NavigationView. I ran into a similar issue with wanting to dismiss the TabView when I navigating but SwiftUI won't let that happen. Each tab needs to be a NavigationView and you need to dismiss the TabView creatively if that is what you want.
TabView {
NavigationView {
ScrollView {
// your view here
}
}.tabItem {
// tab label
}
// etc
}
Essentially the navigation view needs to be a child (in the brackets) of the tab view and the scrollview needs to be the direct child of the navigation view.
Use navigationBarTitle("Title") and navigationBarBackButtonHidden(true) on the TabView's sub-view, not on itself.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
}
.navigationBarTitle("Title")
.navigationBarBackButtonHidden(true)
}
}
}
Im trying to use a simple navigation view, with a navigation link to a different view. When running the app and the navigation link is pressed it takes me to the new view.
However when I'm on a simulator or device the back button dose not work, whereas on the preview it works fine.
Any ideas what the problem may be?
import SwiftUI
struct HomeView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: Text("Detail View")) {
Text("Hello World")
}
}
.navigationBarTitle("SwiftUI")
}
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
I think the problem may be caused by the fact that the HomeView is part of a tabView. The following code is from AppView.swift which is what is run when the app is run (You can see the code for that at the very bottom).
I think this is the problem because when the code bellow is commented out the app works fine.
HomeView()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
.onTapGesture {
self.selectedTab = 2
}
.tag(2)
#main
struct Skate_AppApp: App {
var body: some Scene {
WindowGroup {
HomeView()
}
}
}
From your code I can tell that the problem is in onTapGesture, and I presume from self.selectedTab = 2 that you want to get which tab the user has selected.
Let's refactor your code a little bit with the same concept.
Solution: Delete onTapGesture and add onAppear.
TabView {
HomeView().tabItem {
Image(systemName: "house.fill")
Text("Home")
}.onAppear{
self.selectedTab = 2
}.tag(2)
AnotherView().tabItem {
Image(systemName: "car.fill")
Text("Login View")
}.onAppear{
self.selectedTab = 3
}.tag(3)
}
By this whenever a view appears, it means that the user has selected it, onAppear will be called, and your variable selectedTab will be changed. I hope this answer your question.
I am developing an app with SwiftUI.
I have a NavigationView and I have buttons on the navigation bar. I want to replace the current view (which is a result of a TabView selection) with another one.
Basically, when the user clicks "Edit" button, I want to replace the view with another view to make the edition and when the user is done, the previous view is restored by clicking on a "Done" button.
I could just use a variable to dynamically choose which view is displayed on the current tab view, but I feel like this isn't the "right way to do" in SwiftUI. And this way I could not apply any transition visual effect.
Some code samples to explain what I am looking for.
private extension ContentView {
#ViewBuilder
var navigationBarLeadingItems: some View {
if tabSelection == 3 {
Button(action: {
print("Edit pressed")
// Here I want to replace the tabSelection 3 view by another view temporarly and update the navigation bar items
}) {
Text("Edit")
}
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
TabView(selection: $tabSelection) {
ContactPage()
.tabItem {
Text("1")
}
.tag(1)
Text("Chats")
.tabItem() {
Text("2")
}
.tag(2)
SettingsView()
.tabItem {
Text("3")
}
.tag(3)
}.navigationBarItems(leading: navigationBarLeadingItems)
}
}
}
Thank you
EDIT
I have a working version where I simply update a toggle variable in my button action that makes my view display one or another thing, it is working but I cannot apply any animation effect on it, and it doesn't look "right" in SwiftUI, I guess there is something better that I do not know.
If you just want to add animations you can try:
struct ContentView: View {
...
#State var showEditView = false
var body: some View {
NavigationView {
TabView(selection: $tabSelection) {
...
view3
.tabItem {
Text("3")
}
.tag(3)
}
.navigationBarItems(leading: navigationBarLeadingItems)
}
}
}
private extension ContentView {
var view3: some View {
VStack {
if showEditView {
FormView()
.background(Color.red)
.transition(.slide)
} else {
Text("View 3")
.background(Color.blue)
.transition(.slide)
}
}
}
}
struct FormView: View {
var body: some View {
Form {
Text("test")
}
}
}
A possible alternative is to use a ViewRouter: How To Navigate Between Views In SwiftUI By Using An #EnvironmentObject.
I'm getting a strange crash from pretty normal navigation on my SwiftUI app
I have a simple tab view :
struct FFTabView: View {
var body: some View {
TabView {
LibraryView2()
}
.navigationBarBackButtonHidden(true)
.navigationBarHidden(true)
.navigationBarTitle("", displayMode: .inline)
}
}
// MARK: -
struct LibraryView2: View {
var body: some View {
VStack {
NavigationLink(destination: Foo()) {
Text("go to foo")
}
}
.tabItem {
Image(systemName: "square.grid.2x2.fill")
Text("Skill Library")
}
}
}
struct Foo: View {
var body: some View {
Text("foo view")
}
}
When I go back via my nav bar, from Foo I get a crash: Tried to pop to a view controller that doesn't exist
Any idea what's going on here? I can't find anything related to this and SwiftUI so figured I'd post. Thanks
Although you didn't specify, I assume your FFTabView is wrapped in a NavigationView somewhere.
Ultimately, then, your view hierarchy looks like
NavigationView {
TabView {
NavigationLink {
...
}
}
}
If you restructure your view hierarchy so it's like
TabView {
NavigationView {
NavigationLink {
...
}
}
}
The crash doesn't happen.
Edit:
I have confirmed that it is related to the regression/bug discussed in this answer, introduced in Xcode 11.2. Your original code works fine in Xcode 11.1.