Custom NavigationView + Status Bar for only one view in SwiftUI - swift

I have an application with a blue background main screen and other white background screens. As I'm using custom colors for the entire app, I decided to use .preferredColorScheme(.light) on the root file (SomethingApp.swift) otherwise it would be a complete mess on dark mode.
#main
struct SomethingApp: App {
var body: some Scene {
WindowGroup {
HomeView()
.preferredColorScheme(.light)
}
}
}
But the result for this main screen is particularly ugly!
I get a black status bar on a blue background and the white navigation title is almost invisible on scroll (white navigation background + bottom border).
So, I've looked for a better solution:
1 - Using .preferredColorScheme(.dark) just for this main screen. The result should be a white status bar and a dark NavigationView on scroll. But no matter where I put this modifier, nothing happens!
2 - Customizing the NavigationView inside an init(). It would be awesome to only have the blur effect on scroll or maybe a very light white background so the title could still be visible. But I've only managed to make the background disappears on scroll (see below).
init() {
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithTransparentBackground()
UINavigationBar.appearance().standardAppearance = navBarAppearance
// UINavigationBar.appearance().compactAppearance = navBarAppearance
// UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
}
Do you have any other ideas? Thanks.
EDIT
I have just noticed that this init() is applied to all the views which is not what I wanted lol.
Also, I have just discovered to perform the "1 -" by using .colorScheme(.dark) on the NavigationView but unfortunately it doesn't changed the status bar color. But it's a progress!

Related

SwiftUI not detecting dark mode from setting

In SwiftUI on my device, I set the appearance to dark mode. However, my application doesn't change the background colour based on that appearance. For the color asset, I have set the values for both the light and dark appearance, like so
In my code this is how I set the background color and static variable,
extension Color {
static let backgroundColor = Color("Background")
}
..... // In my View
ZStack {
Color.backgroundColor
.ignoresSafeArea()
......
}
Also when I debug and check
#Environment (\.colorScheme) var colorScheme:ColorScheme
it returns that my device is indeed on dark mode. Any idea what is going on and why I'm unable to activate dark mode automatically from the user's device preferences.
Turns out when you use
.preferredColorScheme(.light | .dark)
It applies that preference to the entire view rather than the element the property is being used on.
This code works fine on simulator and device:
struct ColorSchemeView: View {
var body: some View {
Color.backgroundColor
.ignoresSafeArea()
}
}
fileprivate extension Color {
static let backgroundColor = Color("DarkLight")
}
I suspect you have some other issue in the code that will need a true Minimal Reproducible Example (MRE) to debug.

How can I hide title bar in SwiftUI? (for MacOS app)

there πŸ‘‹
I know hide title bar in Storyboard.
But I can't find the way in SwiftUI.
I want to hide title bar with control buttons and make floating image view.
Let me know please.
If you know related example, tell me please.
My little english sorry.. πŸ™‡
import SwiftUI
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}.windowStyle(HiddenTitleBarWindowStyle())
}
}
try HiddenTitleBarWindowStyle()
Removing the Title Bar in Your Mac App Built with Mac Catalyst
Display content that fills the entire height of a window by removing the title bar.
By default, Mac apps built with Mac Catalyst display a title bar across the top of their windows. A horizontal line separates the title bar from the content of the window.
Some Mac apps such as Messages and Contacts have no title bar in their main window. Instead, the top of the window shows only the Close, Minimize, and Zoom buttons with no separator between them and the window's content. In this UI design, the content area fills the entire height of the window.
The following image illustrates these styles in two windows. The first window displays a title bar, while the second has none.
Screenshot of two windows, one stacked above the other, with a dark background in the content area of each window.
Remove the Title Bar
If you choose to design your window without a title bar, you must remove it from the window. To remove the title bar, set the title bar’s titleVisibility property to UITitlebarTitleVisibility.hidden and the toolbar property to nil. The following code shows how to remove the title bar and its separator from the window during the setup of a new scene.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
#if targetEnvironment(macCatalyst)
if let titlebar = windowScene.titlebar {
titlebar.titleVisibility = .hidden
titlebar.toolbar = nil
}
#endif
}
Click here for more information
I could not find a way to hide the toolbar entirely in SwiftUI. But this is a possible workaround. You can put this code in your AppDelegate file.
func applicationDidFinishLaunching(_ aNotification: Notification) {
let window = NSApplication.shared.windows.first!
window.titlebarAppearsTransparent = true
window.backgroundColor = .white
window.standardWindowButton(.closeButton)!.isHidden = true
window.standardWindowButton(.miniaturizeButton)!.isHidden = true
window.standardWindowButton(.zoomButton)!.isHidden = true
}
Using this code will make it appear that the toolbar is hidden when in reality, it is still there. But the buttons are hidden, and the background is transparent.

How to disable opacity with transitions in SwiftUI?

I'm using SwiftUI.
I have a transition that I'm using to bring up my SignInView. However, this transition seems to be automatically applying an opacity effect on the view it's replacing. This wouldn't be a problem, however, it seems like the Safe Area on both the top and the bottom have different rates of receiving the opacity than the rest of the view.
I'm trying to find 1 of 2 solutions:
How can I get rid of the opacity effect altogether, or
How can I get the opacity effect to be applied evenly everywhere.
Here is the code for my transition:
struct AuthView: View {
#State var showSignIn: Bool = false
var body: some View {
ZStack {
if !showSignIn {
WelcomeView(showSignIn: $showSignIn)
} else {
SignInView(showSignIn: $showSignIn)
.transition(AnyTransition.move(edge: .trailing))
.zIndex(1)
}
}
}
}
Here is the button controlling the state variable:
Button(action: { withAnimation(.easeInOut) { showSignIn.toggle() } }) {
//Button text
}
I also have a video (GIF) that better shows what I'm talking about, when I mention the uneven distribution of opacity.
https://i.stack.imgur.com/KXEcF.gif
If you look closely at the top and bottom safe area it fades faster than the rest of the view, which is undesired.
Note: When I changed the appearance to Dark Mode, it turned black instead of white on the top and bottom.
I assume you want identity transition for original view (opacity transition is applied by default if none other specified)
if !showSignIn {
WelcomeView(showSignIn: $showSignIn)
.transition(.identity) // << here !!

Get update for light/dark mode theme change

I have a SwiftUI app, in which I have a SpriteKit scene. However, when changing between light and dark mode, the scene background does not change colour until the app is reopened.
This is not ideal, so I want to register when the light/dark mode appearance is changed. The colour in my assets has an "any" and a "dark" appearance.
Code inside updateUIView() within a UIViewRepresentable struct, which sets up the scene when it is linked with SwiftUI:
scene.backgroundColor = UIColor(named: "Color.Scene")!
uiView.presentScene(scene)
How do I achieve this? I am using iOS 13's system wide dark mode, so I do not want a solution of Notification Center because all I am looking to do is update the background according to the system settings.
You can use #Environment (\.colorScheme) var colorScheme: ColorScheme in your view and make the views body react to it:
struct ContentView: View {
#Environment (\.colorScheme) var colorScheme: ColorScheme
var body: some View {
HStack {
if self.colorScheme == .light {
// draw for light mode
} else {
// draw for dark mode
}
}
}
}

UINavigationBar titleTextAttributes does not update fully after reloading views

This is a tricky one.
Here is my storyboard for this demo:
The Settings screen segues to the My Color screen where users can choose either a dark or light color scheme for the app. When a change is made, I remove all views from the window and then re-add them to force the current view to immediately apply the changes via the UIAppearance proxy. So the color of the navigation bar and the nav bar's text color both change immediately.
Next, the user unwinds the segue to return to the Settings screen. On the Settings screen, the new color of the navigation bar is already applied. The new color of the nav bar's text is also already applied. However, for a brief instant while the segue is in transition, the nav bar still shows the old text color. The new text color is not shown until after the transition is complete. This results in a minor, but noticable, visual glitch as the nav bar's text suddenly changes from the old color to the new color.
To update the color of the nav bar text when the user flips the switch, I run the following code in the My Color screen's view controller. (The full project code up on Github at https://github.com/prinomen/social_demo2).
func switchValueDidChange(sender:UISwitch!) {
if (sender.on == true) {
colorIndex = 1 // nav bar is now black
UINavigationBar.appearance().barTintColor = black // set appearance proxy to the new color
// Run this switch to set the textColor global var to match the preferred color scheme, based on the value of colorIndex.
switch colorIndex {
case 0: // white
textColor = green
statusBarTextIsBlack = true
case 1: // black
textColor = red
statusBarTextIsBlack = false
default:
break;
}
// Update these appearance proxy items (they need the window to reload before they will manifest their changes).
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
UINavigationBar.appearance().tintColor = textColor
// Remove all views from the window and then re-add them in order to force the current view to immediately apply changes to UIAppearance.
let windows : NSArray = UIApplication.sharedApplication().windows
for window in windows as! [UIWindow] {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
} else {
colorIndex = 0 // nav bar is now white
UINavigationBar.appearance().barTintColor = white // set appearance proxy to the preferred color
// Run this switch to set the textColor global var to match the preferred color scheme, based on the value of colorIndex.
switch colorIndex {
case 0: // white
textColor = green
statusBarTextIsBlack = true
case 1: // black
textColor = red
statusBarTextIsBlack = false
default:
break;
}
// Update these appearance proxy items (they need the window to reload before they will manifest their changes).
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
UINavigationBar.appearance().tintColor = textColor
// Remove all views from the window and then re-add them in order to force the current view to immediately apply changes to UIAppearance.
let windows : NSArray = UIApplication.sharedApplication().windows
for window in windows as! [UIWindow] {
for view in window.subviews {
view.removeFromSuperview()
window.addSubview(view)
}
}
}
}
Aside from changing the color via the appearance proxy, I've also tried setting the color explicitly within the viewWillAppear and viewWillLayoutSubviews methods of the Settings screen view controller by running this line:
self.navigationController?.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "Avenir-Medium", size: 22)!, NSForegroundColorAttributeName: textColor]
But this results in the same issue. What I find confusing is that the other changes made via the appearance proxy are updated without encountering this issue. Only the titleTextAttributes property is troubled by this issue.
I thought that maybe iOS makes some kind of "snapshot" of the Settings screen when segueing to the My Color screen. Then when the segue is reversed, the "snapshot" with the old nav bar text color is used and the new color is not updated until the segue is finished. But if that were true, then why doesn't the navigation bar's barTintColor also experience the same problem? There must be a different way the reverse segue is handled, but I can't seem to figure it out.
Is there a way to apply the color change to the title text before the transition happens, in a way that affects the transition itself?
Thanks for any insight!