How to disable resizability of Sidebar NavigationView in SwiftUI (MacOS)? - swift

I'm new to Swift and I've been trying to build an app and learn as I go. I have a NavigationView that holds a List as a sidebar that renders the content as the user clicks. The bar between these two panes can be grabbed to allow the user to resize. This seems to be the default behavior of a NavigationView. I'm trying to find a way to disable it because I don't want the user to resize the sidebar.
struct Sidebar: View {
var body: some view {
List {
NavigationLink("First section", destination: FirstSection)
}
.frame(minWidth: 150, maxWidth: 150)
}
}
I also couldn't find a way to tell Swift that I want my List view to have a dynamic width that just fits the content. Just like it's done with CSS width: fit-content;
In the picture below, you can see that I was able to resize the sidebar to be almost half the screen. How to disable this behavior?

I do found a solution for that all you have to do is to set the destination width so that the sidebar can't be resized to the destination view for example like that ## consider the firstSection() as a View ##
Here the app main start
import SwiftUI
#main
struct macosTestApp: App {
var body: some Scene {
WindowGroup {
NavigationView {
SideBar()
}.toolbar {
// add the open/close sidebar navigation here
ToolbarItem(placement: .navigation) {
Button(action: toggleSidebar, label: { // 1
Image(systemName: "sidebar.leading")
})
}
}.frame(minWidth: 800, maxWidth: .infinity, minHeight: 600, maxHeight: .infinity, alignment: .center)
}
}
private func toggleSidebar() { // 2
NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil)
}
}
Here the sidebar and the navigationView
import SwiftUI
struct SideBar: View {
#State var isActiveView:Bool = true
var body: some View {
List {
NavigationLink("First section", destination: FirstSection().frame(minWidth: 750, maxWidth: .infinity, minHeight: 600, maxHeight: .infinity, alignment: .center),isActive: $isActiveView)
}
}
}
struct FirstSection: View {
var body: some View {
Text("Hello")
}
}
struct text_Previews: PreviewProvider {
static var previews: some View {
SideBar()
}
}

i was able to do it, in a weird way,
the first thing was to create an NSViewControllerRepresentable
so i was able to wrap an NSViewController in which i generate a 1X1 square view
because the NSViewController has the complete life cycle
i was able to write the code inside viewDidDisappear()
and toggle back the sidebar

Related

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)
}
}
}
}
}

How to change Dimensions of an SwiftUI Default Button on MacOS?

I want to change the behaviour of an SwiftUI Button so that two of them automatic get the same size.
But somehow I always only affect an ParentView of my View.
import SwiftUI
struct ContentView: View {
var body: some View {
HStack {
Button("ABC", action: {return}).frame(maxWidth: .infinity).background(Color.green)
Button("EFCHHSJFJSJDNJS", action: {return}).buttonStyle(MorePaddingButtonStyle()).frame(maxWidth: .infinity).background(Color.yellow)
}.frame(maxWidth: .infinity)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
And it should be sized like this:
The example is located here in an GitHub repo.
Hope someone can help me to resize them with their background/shape (most likely for the default Button).
Use button with label
struct ContentView: View {
var body: some View {
HStack {
Button(action: { print("ABC") }) {
Text("ABC").frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}.buttonStyle(PlainButtonStyle()).background(Color.green)
Button(action: { print("EFCHHSJFJSJDNJS") }) {
Text("EFCHHSJFJSJDNJS").frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
}.buttonStyle(MorePaddingButtonStyle()).background(Color.yellow)
}
}
}
The default button style can not set the width. It automatically set the width according to the button contains. So use PlayStyle or your own MorePaddingButtonStyle.

How to stop the page from being able to be scrolled up and down while using a paged tab view in swiftUI

I am trying to build an app that shows different days of the week in a paged tab view, but when I scroll sideways to a day (e.g. Tuesday), I can scroll it up and down as if it was a scroll view. I don't have a scroll view in my content view.
My code is something like this:
struct ContentView: View {
var body: some View {
TabView {
Text("Saturday")
Text("Sunday")
Text("Monday")
Text("Tuesday")
Text("Wednesday")
Text("Thursday")
Text("Friday")
}
.tabViewStyle(PageTabViewStyle())
}
}
You can do that like this
Put TabView inside the ScrollView with .onAppear()
.onAppear(perform: {
UIScrollView.appearance().alwaysBounceVertical = false
})
struct ContentView: View {
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
TabView {
Text("Saturday")
Text("Sunday")
Text("Monday")
Text("Tuesday")
Text("Wednesday")
Text("Thursday")
Text("Friday")
}
.tabViewStyle(PageTabViewStyle())
.frame(width: 300, height: 600, alignment: .center)
}
.frame(width: 300, height: 600, alignment: .center)
.background(Color.blue)
.onAppear(perform: {
UIScrollView.appearance().alwaysBounceVertical = false
})
}
}

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.

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()
}
}
}
}