Encountering unexpected behavior when List rotates with SwiftUI - iphone

I have setup a relatively basic List embedded in a NavigationView, but when I perform a rotation, things do not work as expected.
The following screenshots show the sequence of events.
When the view is initially presented on an iPhone (running iOS 13.1 17A844)
When the view is rotated from portrait to landscape
My main interest here is that the back button disappears.
Finally, when it is rotated back to portrait
Notice that the Bar Title has shrunk and is now aligned with the back button.
This is a simplified version of the code I am using to generate these screens:
import SwiftUI
struct ViewA: View {
var body: some View {
NavigationView {
VStack {
NavigationLink(destination: ViewB()) {
Text("ViewB")
}
.frame(maxWidth: .infinity,
maxHeight: .infinity,
alignment: .center)
Divider()
NavigationLink(destination: ViewB()) {
Text("ViewB")
}
.frame(maxWidth: .infinity,
maxHeight: .infinity,
alignment: .center)
}
.padding(.all, 10)
.multilineTextAlignment(.center)
.navigationBarTitle(Text("Main blah"))
}
}
}
struct ViewB : View {
private let items = ["A", "B", "C"]
var body: some View {
List () { [items] in
ForEach(items.indices, id: \.self) { index in
NavigationLink(destination: DetailView(detail:
Detail(title: items[index]))) {
Text("Blah blah")
}
}
}
.navigationBarTitle(Text("Bar Title"))
.listStyle(GroupedListStyle())
}
}
Ideally, I would like to have the UI retain the same appearance after a rotation.
For comparison, when I run this on a physical iPad using iOS 13.1 (17A844), it behaves as expected.

Related

Swift UI Navigation View not switching the whole screen

I want to create a simple NavigationView. But with code outside of it. Like this:
struct TestView: View {
var body: some View {
VStack{
RoundedRectangle(cornerRadius: 10)
.frame(width: 100, height: 100)
NavigationView {
VStack{
NavigationLink {
Text("HEllo")
} label: {
Text("Click me")
}
.navigationViewStyle(.columns)
}
.navigationTitle("A Title")
}
}
}
}
I do that so the navigation Title is below the item outside the NavigationView.
This code gives me this:
Image because I am not allowed to insert images yet.
When I click on the NavigationLink though I see this:
The Image
As you see the RoundedRectangle still is viewable at top of the screen. How can I fix that, so that the Rectangle disappears and the Destination is viewable in full screen?
Set navigation view first, You have to put everything inside the navigation view.
NavigationView { // Here
VStack{
RoundedRectangle(cornerRadius: 10)
.frame(width: 100, height: 100)
VStack{ // Remove from above
The RoundedRectangle is still visible because it is outside of the NavigationView, only the content of the NavigationView will move with the NavigationLink
Something you could do is to use the toolbar of the NavigationView to place items on the top of the screen
struct TestView: View {
var body: some View {
NavigationView {
NavigationLink {
Text("HEllo")
} label: {
Text("Click me")
}
.navigationViewStyle(.columns)
.navigationTitle("A Title")
.toolbar {
ToolbarItem {
RoundedRectangle(cornerRadius: 10)
.frame(width: 100, height: 100)
}
}
}
}
}

Change color view on SwiftUI

I can't find a solution to change my background color view, I tried a lot of options and nothing works.
I'm trying solutions but the isn't changing
There is my struct of the code:
struct ContentView: View {
var body: some View {
VStack {
Text("Trying ColorView")
.font(.largeTitle)
.bold()
Button("ColorView") {
}
}
.accentColor(Color.black)
}
}
First of all you have already mistakes in your posted code above, your XCode should normally tell you that.
Which view you want to change..?
This might be a solution... You can change it like you need it.
struct testViewTwo: View {
var body: some View {
NavigationView {
VStack {
VStack(spacing: -15) {
HStack {
HStack {
Text("Hello World")
}.background(Color.blue)
}.foregroundColor(Color.white)
}
}.background(Image("Background"))
}
}
}
You change a background color with the background modifier.
If you want to change the background color of the whole view, you should create a ZStack that spans the whole view with a background modifier.
ZStack {
...
}
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.background(Color(.blue)
You can simply use Color("Green") to change the color. Here's a sample.
var body: some View {
NavigationView{
VStack {
VStack(spacing: 15){
HStack {
Color("Green")
.edgesIgnoringSafeArea(.top)
}
}
}
}
}

SwiftUI macOS Xcode Style Toolbar

I like to recreate a toolbar similar to Apples Notes App using SwiftUI in a macOS app (I am using Xcode 12.3 and macOS 11.1):
My attempt was to use a Navigation View to get the Master/Detail setup (for now I do not need a third panel like the original Notes App has). I am interested in how to get the appearance right, e.g. background color and behavior of the buttons in the toolbar. I tried out some approaches, the best I came up with for the moment is this for the main file:
import SwiftUI
#main
struct App_Without_Name_in_Window_Top_AreaApp: App {
var body: some Scene {
WindowGroup("") { // <-- The ("") will remove the app name in the toolbar
ContentView()
}
.windowToolbarStyle(UnifiedCompactWindowToolbarStyle())
}
}
And for the content view:
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
Text("Master")
.frame(minWidth: 200, maxWidth: 300, minHeight: 300, alignment: .leading)
.padding()
.toolbar {
ToolbarItem(placement: .status) {
Button(action: {
myToggleSidebar()
}) {
Image(systemName: "sidebar.left")
}
}
}
.presentedWindowToolbarStyle(ExpandedWindowToolbarStyle())
Text("Detail")
.frame(minWidth: 200, alignment: .center)
.toolbar {
ToolbarItem(placement: .navigation) {
Button(action: {
print("Button pressed")
}) {
Image(systemName: "bold.italic.underline")
}
}
ToolbarItem(placement: .navigation) {
Button(action: {
print("Button pressed")
}) {
Image(systemName: "lock")
}
}
}
}
.frame(minWidth: 500, minHeight: 300)
}
}
func myToggleSidebar() {
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
which yields a result like this:
Now my question is: How can I alter the color of the left and right parts of the toolbar? I also have problems with the behavior of the toolbar. When the master panel's size is increased, the buttons of the right part of the toolbar are disappearing very early although there is a lot of space left:
What do I have to do to prevent it?
Okay, I found a trick that works:
Set the scene's windowStyle to HiddenTitleBarWindowStyle, which both hides the title and removes the white background:
WindowGroup {
ContentView()
}
.windowToolbarStyle(UnifiedCompactWindowToolbarStyle())
.windowStyle(HiddenTitleBarWindowStyle())
(Note that I don't set the scene name to an empty string, as that's no longer needed and it messed up the window name in the "Window" menu too)
To force a divider between the toolbar and the detail view content, stretch the detail content to fill the whole space and put a Divider behind it:
Text("Detail")
.frame(minWidth: 200, maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(VStack {
Divider()
Spacer()
})
.toolbar { ...
That seems to do it!
What you want is to use
.windowToolbarStyle(UnifiedWindowToolbarStyle(showsTitle: false))
because it preserves the correct behavior when the user tabs the application
Using
.windowToolbarStyle(UnifiedCompactWindowToolbarStyle())
.windowStyle(HiddenTitleBarWindowStyle())
Causes funky behavior when the user opens a new tab due to the coloring of the toolbar.

How to make edgesIgnoringSafeArea in swiftUI work?

The following code makes a view where a rectangle is at the bottom of the view. I put .edgesIgnoringSafeArea(.bottom) so the rectangle goes all the way down but it doesn't work. Im simulating this on an Iphone 11 and always leaves a blank space below.
struct ContentView: View {
var body: some View {
NavigationView {
VStack {
List {
Text("Hello, World!")
}
Spacer()
Rectangle()
.frame(height: 150)
.edgesIgnoringSafeArea(.bottom)
}
}
}
}
The rectangle is inside a VStack, and the VStack doesn't ignore the safe area. Even if the rectangle ignores the safe area, it can't extend beyond its parent to fill the whole screen.
You should put edgesIgnoringSafeArea after the VStack, and the rectangle will naturally fill the VStack, hence filling the whole screen.
var body: some View {
NavigationView {
VStack {
List {
Text("Hello, World!")
}
Spacer()
Rectangle()
.frame(height: 150)
}
.edgesIgnoringSafeArea(.bottom)
}
}

SwiftUI macOS NavigationView Cannot Highlight First Entry

I'm attempting to create a master/detail view on macOS with SwiftUI. When the master/detail view first renders, I'd like it to immediately "highlight" / "navigate to" its first entry.
In other words, I'd like to immediately render the following: master/detail first row highlighted
I'm using NavigationView and NavigationLink on macOS to render the master/detail view:
struct ContentView: View {
var body: some View {
NavigationView {
List {
NavigationLink(destination: Text("detail-1").frame(maxWidth: .infinity, maxHeight: .infinity)) {
Text("link-1")
}
NavigationLink(destination: Text("detail-2").frame(maxWidth: .infinity, maxHeight: .infinity)) {
Text("link-2")
}
}
}
}
}
I've tried using both the isActive and the tag / selection options provided by NavigationLink with no luck. What might I be missing here? Is there a way to force focus on the first master/detail element using SwiftUI?
I came across this problem recently and after being stuck at the same point I found Apple's tutorial which shows that you don't use NavigationLink on macOS.
Instead you just create a NavigationView with a List and a DetailView. Then you can bind the List's selection and it works properly.
There still seems to be a bug with the highlight. The workaround is setting the selection in the next run loop after the NavigationView has appeared. :/
Here's a complete example:
enum DetailContent: Int, CaseIterable, Hashable {
case first, second, third
}
extension DetailContent: Identifiable {
var id: Int { rawValue }
}
struct DetailView: View {
#Binding var content: DetailContent?
var body: some View {
VStack {
Text("\(content?.rawValue ?? -1)")
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct MainView: View {
#State var detailContent: DetailContent?
var body: some View {
NavigationView {
List(selection: $detailContent) {
Section(header: Text("Section")) {
ForEach(DetailContent.allCases) { item in
Text("\(item.rawValue)")
.tag(item)
}
}
}
.frame(minWidth: 250, maxWidth: 350)
.listStyle(SidebarListStyle())
if detailContent != nil {
DetailView(content: $detailContent)
}
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.onAppear {
DispatchQueue.main.async {
self.detailContent = DetailContent.allCases.randomElement()
}
}
}
}