Opaque background Toolbar when scrolling - swiftui

I have opaque the background of the TabBar but not the ToolBar when I expected both...
.onAppear{
let tabBarAppearance = UITabBarAppearance()
tabBarAppearance.configureWithOpaqueBackground()
UITabBar.appearance().standardAppearance = tabBarAppearance
let toolBarApperance = UIToolbarAppearance()
toolBarApperance.configureWithOpaqueBackground()
UIToolbar.appearance().standardAppearance = toolBarApperance
UIToolbar.appearance().scrollEdgeAppearance = toolBarApperance
}
Some hint plis ;)

toolbarBackground(_:for:) solves the problem but only in iOS 16
if #available(iOS 16.0, *) {
...
.toolbarBackground(Color.black, for: .automatic)
}

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 subview going off of the window

As a SwiftUI beginner, I have just recently started creating my first MacOS app. However, when I was trying to implement the NSVisualEffectView to blur the background, the contentView that I was using went off of the screen, which wasn't visible at all. It looked something like this:
So I tried fixing the size of the text I had in the contentView by making the text I had into Text("Hello world!").frame(width: 700, height:500), and the screen became like this:
By doing this, in the bottom left corner, the tiny shapes of Hello world! can be seen. However, unless I position the text on the top right of the contentView, I can't seem to move it. Does anyone know how to fix this?
*For reference, here is the AppDelegate.swift contents:
import SwiftUI
#main
class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView()
let visualEffect = NSVisualEffectView()
visualEffect.blendingMode = .behindWindow
visualEffect.state = .active
visualEffect.material = .fullScreenUI
visualEffect.addSubview(NSHostingView(rootView: contentView))
// Create the window and set the content view.
window = NSWindow(
contentRect: .zero,
styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
backing: .buffered, defer: false)
window.isReleasedWhenClosed = false
window.center()
window.setFrameAutosaveName("Main Window")
window.contentView = visualEffect
window.makeKeyAndOrderFront(nil)
window.titlebarAppearsTransparent = true
window.titleVisibility = .hidden
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
}

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

Spacing around NSTextAttachment

How do I make spacing around NSTextAttachments like in the example below?
In the example No spacing is the default behaviour I get when I append a NSTextAttachment to a NSAttributedString.
This worked for me in Swift
public extension NSMutableAttributedString {
func appendSpacing( points : Float ){
// zeroWidthSpace is 200B
let spacing = NSAttributedString(string: "\u{200B}", attributes:[ NSAttributedString.Key.kern: points])
append(spacing)
}
}
The above answers no longer worked for me under iOS 15. So I ended up creating and adding an empty "padding" attachment in between the image attachment and attributed text
let padding = NSTextAttachment()
//Use a height of 0 and width of the padding you want
padding.bounds = CGRect(width: 5, height: 0)
let attachment = NSTextAttachment(image: image)
let attachString = NSAttributedString(attachment: attachment)
//Insert the padding at the front
myAttributedString.insert(NSAttributedString(attachment: padding), at: 0)
//Insert the image before the padding
myAttributedString.insert(attachString, at: 0)
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] init];
// append NSTextAttachments instance to attributedText...
// then add non-printable string with NSKernAttributeName attributes
unichar c[] = { NSAttachmentCharacter };
NSString *nonprintableString = [NSString stringWithCharacters:c length:1];
NSAttributedString *spacing = [[NSAttributedString alloc] initWithString:nonprintableString attributes:#{
NSKernAttributeName : #(4) // spacing in points
}];
[attributedText appendAttributedString:spacing];
// finally add other text...
Add spacing to image(For iOS 15).
extension UIImage {
func imageWithSpacing(insets: UIEdgeInsets) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(
CGSize(width: self.size.width + insets.left + insets.right,
height: self.size.height + insets.top + insets.bottom), false, self.scale)
UIGraphicsGetCurrentContext()
let origin = CGPoint(x: insets.left, y: insets.top)
self.draw(at: origin)
let imageWithInsets = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return imageWithInsets
}
}
This extension makes it easy to add the space you need. It also works on iOS 16.
extension NSMutableAttributedString {
func appendSpace(_ width: Double) {
let space = NSTextAttachment()
space.bounds = CGRect(x:0, y: 0, width: width, height: 0)
append(NSAttributedString(attachment: space))
}
}
// usages
let example = NSMutableAttributedString()
example.appendSpace(42)
...

changing the height of UITabBar in iOS7/8?

I am trying to change the height of the stock UITabBar to 44px, similar to Tweetbot's tab bar height. I've also seen a few other apps do this as well.
however, when i try to set the height it still remains the same
self.tabBar.frame.height = 40
are we not allowed to change the tab bar height? and if so what is a good alternative? using a toolbar?
It seems everybody says this can't be done easily
In your storyboard give your UITabBar a custom subclass name, then implement the subclass with the following
This tells all views that use the tab bar that it should be a certain height.
#implementation MyTabBar
-(CGSize)sizeThatFits:(CGSize)size
{
CGSize sizeThatFits = [super sizeThatFits:size];
sizeThatFits.height = 100;
return sizeThatFits;
}
#end
SomeGuy's answer above worked for me. Here's the Swift translation for anyone who may need it. I made the height close to what it seems most popular apps use.
class TabBar: UITabBar {
override func sizeThatFits(size: CGSize) -> CGSize {
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = 38
return sizeThatFits
}
}
For Swift 3 and xcode 8
extension UITabBar {
override open func sizeThatFits(_ size: CGSize) -> CGSize {
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = 80 // adjust your size here
return sizeThatFits
}
}
In your UITabBarController
- (void)viewWillLayoutSubviews {
CGRect tabFrame = self.tabBar.frame;
tabFrame.size.height = 80;
tabFrame.origin.y = self.view.frame.size.height - 80;
self.tabBar.frame = tabFrame;
}
In swift it is even simpler than all solutions suggested above by using an extensions to UITabBar, no subclassing necessary:
extension UITabBar {
override public func sizeThatFits(size: CGSize) -> CGSize {
super.sizeThatFits(size)
var sizeThatFits = super.sizeThatFits(size)
sizeThatFits.height = <Insert your height here>
return sizeThatFits
}
}
If you have auto layout enabled, you will need to override instrinsicContentSize instead
Proper usage of intrinsicContentSize and sizeThatFits: on UIView Subclass with autolayout
class TabBar: UITabBar {
override func intrinsicContentSize() -> CGSize {
var intrinsicSize = super.frame.size
intrinsicSize.height = 120
return intrinsicSize
}
}
The developer doesn't own the tabBar, the framework does. It will fight you to make sure that the tabBar stays the same height. If you want to work around this, you can make your own toolbar and add autlayout constraints to its height to force it to stay whatever height you'd like.
If you are on iOS 11 then following will help
-(CGSize)sizeThatFits:(CGSize)size
{
CGSize sizeThatFits = [super sizeThatFits:size];
sizeThatFits.height = 60;
if (#available(iOS 11.0, *)) {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
CGFloat bottomPadding = window.safeAreaInsets.bottom;
sizeThatFits.height += bottomPadding;
}
return sizeThatFits;
}
Basically need to cover safe area, else tabbar height on iPhone X appears to be low.
For iOS11 and above you can use below:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
if (#available(iOS 11.0, *)) {
self.additionalSafeAreaInsets = UIEdgeInsetsMake(0, 0, 20, 0);
}
}
And for all os, create UITabBar subclass, use it in the UITabBarController and implement below method in the implementation of custom tab bar class:
-(CGSize)sizeThatFits:(CGSize)size {
CGSize sizeThatFits = [super sizeThatFits:size];
sizeThatFits.height = kBarHeight;
return sizeThatFits;
}