SWIFTUI - custom color set - swiftui

I'm using this Initialiser to style my navigationBarTitle. It works fine but in darkmode it doesnt look right. Is there a way to change the colors below to use color sets so it looks fine in darkmode?
// Custom Header Initialiser.
init() {
UINavigationBar.appearance().backgroundColor = .white
UINavigationBar.appearance().largeTitleTextAttributes = [
.foregroundColor: UIColor.black,
.font : UIFont(name:"HelveticaNeue-Medium", size: 24)!]
UINavigationBar.appearance().titleTextAttributes = [
.font : UIFont(name: "HelveticaNeue-Medium", size: 18)!]
}
//

Create your custom color set in Assets catalog (eg. named MyColor) and assign your variants for needed appearances, eg. as below
Assing your custom color as below
UINavigationBar.appearance().backgroundColor = UIColor(named: "MyColor")

Related

SwiftUI - NSFontPanel and Color Picker

I am trying to get NSFontPanel/NSFontManager to work in a SwiftUI Document Template app. I have the following which is a customize version of one I found on GitHub. This lets me pick the size, face, style, etc.
Interestingly, a color picker is included in the FontPanel. The documentation doesn't seem to say this. Is this something new?
Anyway, I would like to either be able to use the color picker to let the user select a color, or if not I would like to hide the color picker - at is not "critical" to this application. I am using this to allow customization of text in a sidebar, so color is nice, but not necessary. Currently the Font settings are working, but the color selection displays, and let you pick on, but it always returns System Color.
Any help would be appreciated.
NOTE: I didn't include the FontPickerDelegate, it just calls this:
public struct FontPicker: View{
let labelString: String
#Binding var font: NSFont
#State var fontPickerDelegate: FontPickerDelegate?
public init(_ label: String, selection: Binding<NSFont>) {
self.labelString = label
self._font = selection
}
let fontManager = NSFontManager.shared
let fontPanel = NSFontPanel.shared
#AppStorage("setSidebarFont") var setSidebarFont = "System"
#AppStorage("setSidebarFontSize") var setSidebarFontSize = 24
#AppStorage("setSidebarFontColor") var setSidebarFontColor = "gray"
public var body: some View {
HStack {
Text(labelString)
Button {
if fontPanel.isVisible {
fontPanel.orderOut(nil)
return
}
self.fontPickerDelegate = FontPickerDelegate(self)
fontManager.target = self.fontPickerDelegate
fontManager.action = #selector(fontPickerDelegate?.changeAttributes)
fontPanel.setPanelFont(self.font, isMultiple: false)
fontPanel.orderBack(nil)
} label: {
Text("Font Selection: \(setSidebarFont)")
.font(.custom(setSidebarFont, size: CGFloat(setSidebarFontSize)))
}
}
}
func fontSelected() {
self.font = fontPanel.convert(self.font)
setSidebarFont = self.font.displayName ?? "System"
setSidebarFontSize = Int(self.font.pointSize)
var newAttributes = fontManager.convertAttributes([String : AnyObject]())
newAttributes["NSForegroundColorAttributeName"] = newAttributes["NSColor"]
newAttributes["NSUnderlineStyleAttributeName"] = newAttributes["NSUnderline"]
newAttributes["NSStrikethroughStyleAttributeName"] = newAttributes["NSStrikethrough"]
newAttributes["NSUnderlineColorAttributeName"] = newAttributes["NSUnderlineColor"]
newAttributes["NSStrikethroughColorAttributeName"] = newAttributes["NSStrikethroughColor"]
print("\(newAttributes["NSForegroundColorAttributeName"]!)")
}
}

SwiftUI Size to fit or word-wrap navigation title

I have a navigation title that is too large on some smaller devices.
I've read many ways to set the titleTextAttributes and largeTitleTextAttributes of the UINavigationBar.appearance() however when setting the paragraph style to word wrap, it seems to remove the standard ... clipping and have the text continue off the edge of the screen without wrapping:
init() {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineBreakMode = .byWordWrapping
UINavigationBar.appearance().largeTitleTextAttributes = [
.paragraphStyle: paragraphStyle
]
}
I want to maintain the SwiftUI behaviour where the title is shown as large text until the view is scrolled up and it moves to the navigation bar, so getting the .toolbar directly won't help.
I also don't want to just specify a smaller font as I only want it to shrink or wrap if necessary.
Has anyone managed to achieve this?
You can add this line on the initializer of the view where yo have the issue
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true
Example:
struct YourView: View {
init() {
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true
}
var body: some View {
NavigationView {
Text("Your content")
.navigationBarTitle("Very very large title to fit in screen")
}
}
}

iOS15 Xcode 13 Global Accent Color not working

I'm using the latest beta of Xcode 13 with an app for iOS 14 and now I'm facing this strange issue:
The global accent color of my app was working fine until the iOS 15 update when the color is now set as the default blue where before it was my custom color.
Here is the asset catalog:
This is my project settings page where you can see that the accent color is correct.
And this is what the app looks like when built. The color is the default blue when it needs to be a really dark blue/purple color.
We were using the UIAppearance API in our app. We were not setting the tint color, but somehow calling any of the UIAppearance API's after the app has finished launching causes this behavior.
enum AppAppearance {
static func configure() {
configureCustomBarApperance()
UITableView.appearance().backgroundColor = UIColor(named: .primaryBackground)
UITextView.appearance().backgroundColor = nil
UIScrollView.appearance().keyboardDismissMode = .interactive
}
static func configureCustomBarApperance() {
let barAppearance = UIBarAppearance()
barAppearance.configureWithTransparentBackground()
barAppearance.backgroundColor = UIColor(named: .primaryBackground)
// Toolbars
let toolbarAppearance = UIToolbarAppearance(barAppearance: barAppearance)
UIToolbar.appearance().standardAppearance = toolbarAppearance
UIToolbar.appearance().compactAppearance = toolbarAppearance
UIToolbar.appearance().scrollEdgeAppearance = toolbarAppearance
// Navigation Bars
let navBarAppearance = UINavigationBarAppearance(barAppearance: barAppearance)
navBarAppearance.titleTextAttributes[.foregroundColor] = UIColor.secondaryLabel
UINavigationBar.appearance().standardAppearance = navBarAppearance
UINavigationBar.appearance().compactAppearance = navBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
// Tab Bars
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithTransparentBackground()
tabBarAppearance.backgroundColor = UIColor(named: .secondaryBackground)
UITabBar.appearance().standardAppearance = tabBarAppearance
UITabBar.appearance().scrollEdgeAppearance = tabBarAppearance
}
}
Our solution was to move all of the UIAppearance API work to the initializer of the AppDelegate and this fixed the issue for us. So instead of calling AppAppearance.configure() after the app finished launching...we call it from AppDelegate.init and our Global Accent Color is now being honored.
Don't ask me why...I couldn't tell you.
I finally found a temporary workaround on this AppleDeveloperForum Thread
credit to: #chad_sykes for this answer
I found an alternate solution, which was to set the .accentColor on the main view in the WindowGroup and it gets used across the app.
#main
struct CurvApp: App {
var body: some Scene {
WindowGroup {
myMainView
.accentColor(CurvGlobalAppearance.curvAccentColor)
}
}
}

Modify view from within a view modifier

I have a HighlightedText View which is initialized with a string, a default font and a highlight font. The string can have markers to indicate which portions of it need to be highlighted. That all works fine. Now, instead of initialize it with a default font and a highlight font, I would like to be able to write this in a more swift-ui way to allow for more flexibility and improve readiness. So I would like to be able to do something like this:
HighlightedText("My text <highlighted>")
.defaultFont(.body)
.highlightFont(.title)
I know the standard way should be using a ViewModifier, however all I get from within its body function is a Content type and there doesn't seem to be a way I could cast it into my HighlightedText view and configure it as needed. All I seem to be able to do from within its body is just call other modifiers from View protocol, but that's not enough for my use case.
I've tried this extension, where defaultFont is a file private #State property defined in HighlightedText:
extension HighlightedText {
func defaultFont(_ font: Font) -> some View {
defaultFont.font = font
return body
}
}
That however, does not work. The default font I pass over never gets applied.
Here is possible solution:
if to you have declaration like
struct HighlightedText: View {
var defaultFount = Font.body
var highlightedFont = Font.headline
// ... other code
then your custom modifiers could be as
extension HighlightedText {
func defaultFont(_ font: Font) -> Self {
var view = self
view.defaultFount = font
return view
}
func highlightedFont(_ font: Font) -> Self {
var view = self
view.highlightedFont = font
return view
}
}

How to change background color for tab in tvOS 13?

TvOS 13. I have a UITabBarController with tabs. And can customize almost everything except this obvious thing: focused tab's background. It's always white.
Guide tells
Specify tints for selected and unselected items
I tried:
view.backgroundColor = .purple
tabBar.tintColor = .yellow
tabBar.barTintColor = .red
tabBar.unselectedItemTintColor = .brown
tabBar.backgroundColor = .green
tabBar.backgroundImage = UIColor.blue.toImage()
tabBar.shadowImage = UIColor.orange.toImage()
tabBar.selectionIndicatorImage = UIColor.burgundy.toImage()
Nothing helped.
After playing a bit with various properties of UITabBar and UITabBarController, I finally figured it out.
The property to change focused items background color is selectionIndicatorTintColor of UITabBarAppearance (documentation).
Since it is available on tvOS >= 13.0, you will have to wrap the assignment like this:
if #available(tvOS 13.0, *) {
tabBar.standardAppearance.selectionIndicatorTintColor = .white
}
For #davidv and other folks, here is my solution:
extension UIView {
func subviews<T:UIView>(ofType type: T.Type) -> [T] {
var result = self.subviews.compactMap { $0 as? T }
for sub in self.subviews {
result.append(contentsOf: sub.subviews(ofType: type))
}
return result
}
}
extension UIViewController {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
// перекраска кнопки
let allSubviews = tabBar.subviews(ofType: UIView.self)
let whiteSubviews = allSubviews.filter { $0.backgroundColor == .white }
for s in whiteSubviews {
s.backgroundColor = .gold
}
}
}
UPDATE:
For coloring text:
item.setTitleTextAttributes([NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: colorSelected], for: [.focused])
item.setTitleTextAttributes([NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: colorSelected], for: [.highlighted])
item.setTitleTextAttributes([NSAttributedString.Key.font: font, NSAttributedString.Key.foregroundColor: colorUnselected], for: [.normal])
For coloring background:
tabBar.standardAppearance.selectionIndicatorTintColor = .gold
I accomplish this through a UITabBar extension. The view that is displayed on focus contains a UIMotionEffect so we check against that to find it.
#available(tvOS 13.0, *)
extension UITabBar {
var focusBackgroundView: UIView? {
let allSubviews: [UIView] = subviews.flatMap { [$0] + $0.subviews as [UIView] }
return allSubviews.first{ !$0.motionEffects.isEmpty }
}
}
Usage:
myTabBar.focusBackgroundView.backgroundColor = .red