Custom Navigation Title - SWIFT - swift

I would like to customize my navigation title but run into a problem. "String is not identical to NSObject". Can someone point me in the right direction? My code is below,
let font = UIFont(name: "HelveticaNeue", size: 15.0)
let textFont = [NSFontAttributeName: font]
let navText = [NSAttributedString(string: "MY STRING HERE", attributes: textFont)]
var navString = UILabel()
navString.appendAttributedString(navText)
self.navigationItem.titleView = navString
UPDATE: I was able to solve the problem with the following code,
var navString: NSString = "MY STRING HERE"
var completedNavString = NSMutableAttributedString()
completedNavString = NSMutableAttributedString(string: navString as String, attributes: [NSFontAttributeName:UIFont(name: "Georgia", size: 18.0)!])
var navLabel = UILabel()
navLabel.attributedText = completedNavString
navLabel.sizeToFit()
self.navigationItem.titleView = navLabel

var navString = UILabel()
navString.appendAttributedString(navText)
You can't "append" anything to a UILabel. Try navString.attributedText = navText.

Related

How can you change the Header View title font on FSCalendar and add a Subtitle?

I am trying to set the header title font to a much larger style font more like a big banner, and set a subtitle underneath it of the year.
extension FSCalendar {
func customizeCalendar() {
appearance.caseOptions = [.headerUsesUpperCase]
appearance.headerDateFormat = "MMM"
headerHeight = 100
let header = FSCalendarHeaderView()
header.largeContentTitle?.append("aldjsf")
appearance.headerTitleFont = UIFont(name: "SFProDisplay-Bold", size: 200)
appearance.headerTitleColor = COLOR_BLACK
appearance.headerTitleOffset = CGPoint(x: 0, y: 0)
appearance.headerMinimumDissolvedAlpha = 0.6
appearance.todayColor = COLOR_PRIMARY
appearance.todaySelectionColor = COLOR_BLACK
appearance.titleFont = UIFont(name: "SFProDisplay-Bold", size: 11)
appearance.titleSelectionColor = COLOR_BLACK
appearance.weekdayFont = UIFont(name: "SFProText-Semibold", size: 11)
appearance.weekdayTextColor = COLOR_GREY
appearance.eventDefaultColor = COLOR_BLACK
appearance.subtitleFont = UIFont(name: "SFProDisplay-Bold", size: 20)
appearance.selectionColor = COLOR_BLACK
}
}
Even though I am accessing the property .headerTitleFont it doesn't do anything ? I have tried all kinds of sizes. Any help appreciated, thank you.
Issue with the Font sizes
Seems the font you have mentioned is not available in the simulator/device thus it defaults to a font. I tried your approach with a font which is pre-installed and the headerTitle got changed as expected. List of pre-installed fonts
appearance.headerTitleFont = UIFont(name: "Noteworthy Light", size: 60)
This is how it appears with the above font
Adding a subtitle to the headerView
With the available API, it seems you cannot set a subtitle in the headerView. But alternatively you can achieve it like below by customizing the FSCalendarHeaderCell.titleLabel.attributedText. Note that below code only change the text of collectionView.visibleCells, so you will also have to execute this code when you scroll the headerView
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
for cell in calendar.calendarHeaderView.collectionView.visibleCells {
//create an attributedString with two lines and different font sizes
let attributedString = NSMutableAttributedString(string: "Sep\n2021\n")
let attributes0: [NSAttributedString.Key : Any] = [
.foregroundColor: UIColor.yellow,
.font: UIFont(name: "HelveticaNeue", size: 40)!
]
let attributes1: [NSAttributedString.Key : Any] = [
.foregroundColor: UIColor.systemGray2
]
attributedString.addAttributes(attributes0, range: NSRange(location: 0, length: 3))
attributedString.addAttributes(attributes1, range: NSRange(location: 4, length: 4))
//replace titleLabel attributedText with the one we created
(cell as! FSCalendarHeaderCell).titleLabel.attributedText = attributedString
}
}
This is how it appears after changing the attributedText of the FSCalendarHeaderCell.titleLabel

How to add strings with different fonts in UItextField?

I'm trying to add two part of text with different font parameters.
But they always get value from first string.
And there one strange thing, font color is set independently.
let descriptionTextView: UITextView = {
let view = UITextView()
let attributedText = NSMutableAttributedString(string: "Text", attributes:
[.font:UIFont.boldSystemFont(ofSize: 20),
.foregroundColor: UIColor.blue])
attributedText.append(NSAttributedString(string: "\n\n\n Other text", attributes:
[.font: UIFont.italicSystemFont(ofSize: 5),
.foregroundColor: UIColor.gray]))
view.attributedText = attributedText
view.translatesAutoresizingMaskIntoConstraints = false
view.textAlignment = .center
view.font = UIFont.boldSystemFont(ofSize: 20)
view.isEditable = false
view.isScrollEnabled = false
return view
}()
This is how it look in simulator
Remove
view.font = UIFont.boldSystemFont(ofSize: 20)

Color only underline in Swift

How do i change the color of my underline in a label? I want only the underline to change color, and not the entire text.
I have used this code to get the underline:
let underlineAttribute = [NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue]
let underlineAttributedString = NSAttributedString(string: "\(nearSavings[indexPath.row]) ,-", attributes: underlineAttribute)
cell.detailTextLabel?.attributedText = underlineAttributedString
But i cant find the code, to set the underline color. Anyone who can help?
Swift 4 Solution
You must use NSAttributedString with an array of attributes as [NSAttributedStringKey : Any].
Sample code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Colored Underline Label
let labelString = "Underline Label"
let textColor: UIColor = .blue
let underLineColor: UIColor = .red
let underLineStyle = NSUnderlineStyle.styleSingle.rawValue
let labelAtributes:[NSAttributedStringKey : Any] = [
NSAttributedStringKey.foregroundColor: textColor,
NSAttributedStringKey.underlineStyle: underLineStyle,
NSAttributedStringKey.underlineColor: underLineColor
]
let underlineAttributedString = NSAttributedString(string: labelString,
attributes: labelAtributes)
myLabel.attributedText = underlineAttributedString
}
}
Another solution could be to add a single line border under the label, that acts as an underline.
Get reference to the label
#IBOutlet weak var myLabel: UILabel!
Add the border under the label
let labelSize = myLabel.frame.size
let border = CALayer()
let w = CGFloat(2.0)
border.borderColor = UIColor.yellow.cgColor // <--- Here the underline color
border.frame = CGRect(x: 0, y: labelSize.height - w, width: labelSize.width, height: labelSize.height)
border.borderWidth = w
myLabel.layer.addSublayer(border)
myLabel.layer.masksToBounds = true
Note: with this workaround you underline the whole label. If you need to partially undlerline the text this solution is not appropiate
Swift 5 Solution
class UnderlinedLabel: UILabel {
override var text: String? {
didSet {
guard let text = text else { return }
let textColor: UIColor = .black
let underLineColor: UIColor = .lightGray
let underLineStyle = NSUnderlineStyle.single.rawValue
let labelAtributes:[NSAttributedString.Key : Any] = [
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.underlineStyle: underLineStyle,
NSAttributedString.Key.underlineColor: underLineColor
]
self.attributedText = NSAttributedString(string: text, attributes: labelAtributes)
}
}
}
Usage:
Just give class UnderlinedLabel to your label in the storyboard
The NSAttributedStringKey.underlineColor attribute does what you want:
let underlineAttributes = [
NSAttributedStringKey.underlineStyle: NSUnderlineStyle.styleSingle.rawValue,
NSAttributedStringKey.underlineColor: UIColor.orange
] as [NSAttributedStringKey : Any]
let underlineAttributedString = NSAttributedString(string: "Test", attributes: underlineAttributes)
This will set the underline color to orange whereas the text color will remain black.

Add subtitle under the title in navigation bar controller in Xcode

So I'm wanting to add a "subtitle" under the title in the navigation bar in navigation controller.
Mostly everything I look up so far wants me to use CGRect. I don't know a whole lot what that is and it sounds like its wanting me to create an entire new view which is not what I am wanting to do.
My question is, is there a dot method to adding a subtitle view easily?
The closest thing I found was posted on stack overflow and here is the link:
Create a subtitle in navigationbar
Apparently last year this worked but now I am getting errors and it's in my viewDidLoad...
I tried this:
self.navigationController?.navigationItem.prompt = "Subtitle Here"
It's the only thing that won't show any errors but still doesn't work. It literally does nothing. At least nothing visible at run time.
On a side note, swift is preferred. Thanks!
Here is my version using a stack view on an extension.
extension UINavigationItem {
func setTitle(title:String, subtitle:String) {
let one = UILabel()
one.text = title
one.font = UIFont.systemFont(ofSize: 17)
one.sizeToFit()
let two = UILabel()
two.text = subtitle
two.font = UIFont.systemFont(ofSize: 12)
two.textAlignment = .center
two.sizeToFit()
let stackView = UIStackView(arrangedSubviews: [one, two])
stackView.distribution = .equalCentering
stackView.axis = .vertical
stackView.alignment = .center
let width = max(one.frame.size.width, two.frame.size.width)
stackView.frame = CGRect(x: 0, y: 0, width: width, height: 35)
one.sizeToFit()
two.sizeToFit()
self.titleView = stackView
}
}
Though there is a solution but it has some known issues
Solution is writing a function like this
func setTitle(title:String, subtitle:String) -> UIView {
let titleLabel = UILabel(frame: CGRectMake(0, -2, 0, 0))
titleLabel.backgroundColor = UIColor.clearColor()
titleLabel.textColor = UIColor.grayColor()
titleLabel.font = UIFont.boldSystemFontOfSize(17)
titleLabel.text = title
titleLabel.sizeToFit()
let subtitleLabel = UILabel(frame: CGRectMake(0, 18, 0, 0))
subtitleLabel.backgroundColor = UIColor.clearColor()
subtitleLabel.textColor = UIColor.blackColor()
subtitleLabel.font = UIFont.systemFontOfSize(12)
subtitleLabel.text = subtitle
subtitleLabel.sizeToFit()
let titleView = UIView(frame: CGRectMake(0, 0, max(titleLabel.frame.size.width, subtitleLabel.frame.size.width), 30))
titleView.addSubview(titleLabel)
titleView.addSubview(subtitleLabel)
let widthDiff = subtitleLabel.frame.size.width - titleLabel.frame.size.width
if widthDiff < 0 {
let newX = widthDiff / 2
subtitleLabel.frame.origin.x = abs(newX)
} else {
let newX = widthDiff / 2
titleLabel.frame.origin.x = newX
}
return titleView
}
Using this function for custom navigation title view in viewDidLoad
self.navigationItem.titleView = setTitle("Title", subtitle: "SubTitle")
Only known issue is that if subtitle becomes very large than the misplacement occurs.
Final Outcome
Source: https://gist.github.com/nazywamsiepawel/0166e8a71d74e96c7898
#iosjillian's Swift 4 extension works great, adding a bit more to honor the bar's appearance and user font preferences:
import UIKit
extension UINavigationItem {
func setTitle(_ title: String, subtitle: String) {
let appearance = UINavigationBar.appearance()
let textColor = appearance.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor ?? .black
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
titleLabel.textColor = textColor
let subtitleLabel = UILabel()
subtitleLabel.text = subtitle
subtitleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.subheadline)
subtitleLabel.textColor = textColor.withAlphaComponent(0.75)
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.distribution = .equalCentering
stackView.alignment = .center
stackView.axis = .vertical
self.titleView = stackView
}
}
Thanks a lot for your answer! #RajanMaheshwari
Your coding worked perfectly except the if statement you made with the widthDiff..
I adjusted it a little bit and everything worked smoothly.
if widthDiff < 0 {
let newX = widthDiff / 2
subtitleLabel.frame.origin.x = abs(newX)
} else {
let newX = widthDiff / 2
titleLabel.frame.origin.x = newX
}
Thanks again for your response!
I really liked #user2325031's answer, but found that sizing the labels to fit and setting the frame wasn't needed. I also set the stackView's alignment to .center per #GerardoMR's suggestion.
extension UINavigationItem {
func setTitle(_ title: String, subtitle: String) {
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = .systemFont(ofSize: 17.0)
titleLabel.textColor = .black
let subtitleLabel = UILabel()
subtitleLabel.text = subtitle
subtitleLabel.font = .systemFont(ofSize: 12.0)
subtitleLabel.textColor = .gray
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.distribution = .equalCentering
stackView.alignment = .center
stackView.axis = .vertical
self.titleView = stackView
}
}
In case anyone looking for Objective-C code of the above mentioned solution:
UILabel *title = [[UILabel alloc]init];
UILabel *subtitle = [[UILabel alloc]init];
[title setFont:[UIFont systemFontOfSize:12]];
[title setTextColor:[UIColor whiteColor]];
[title setFont:[UIFont systemFontOfSize:17]];
[title sizeToFit];
title.text = #"Title";
[subtitle setTextColor:[UIColor whiteColor]];
[subtitle setFont:[UIFont systemFontOfSize:12]];
[subtitle setTextAlignment:NSTextAlignmentCenter];
[subtitle sizeToFit];
subtitle.text = #"Subtitle Title";
UIStackView *stackVw = [[UIStackView alloc]initWithArrangedSubviews:#[title,subtitle]];
stackVw.distribution = UIStackViewDistributionEqualCentering;
stackVw.axis = UILayoutConstraintAxisVertical;
stackVw.alignment =UIStackViewAlignmentCenter;
[stackVw setFrame:CGRectMake(0, 0, MAX(title.frame.size.width, subtitle.frame.size.width), 35)];
self.navigationItem.titleView = stackVw;
Thanks for the answer #RajanMaheshwari
If anyone is having the issue where the title becomes misaligned when the subtitle text is longer than the title text, I added the following code to the Rajan's answer above just below where the subtitleLabel is instantiated:
// Fix incorrect width bug
if (subtitleLabel.frame.size.width > titleLabel.frame.size.width) {
var titleFrame = titleLabel.frame
titleFrame.size.width = subtitleLabel.frame.size.width
titleLabel.frame = titleFrame
titleLabel.textAlignment = .center
}
Hope this helps someone who encountered the same issue as me
Another solution, using only one label and NSAttributedString to differentiate between title and subtitle (with different font sizes, weights, colors, etc.) instead. Removes the problem of different label alignment.
extension UIViewController {
func setTitle(_ title: String, subtitle: String) {
let rect = CGRect(x: 0, y: 0, width: 400, height: 50)
let titleSize: CGFloat = 20 // adjust as needed
let subtitleSize: CGFloat = 15
let label = UILabel(frame: rect)
label.backgroundColor = .clear
label.numberOfLines = 2
label.textAlignment = .center
label.textColor = .black
let text = NSMutableAttributedString()
text.append(NSAttributedString(string: title, attributes: [.font : UIFont.boldSystemFont(ofSize: titleSize)]))
text.append(NSAttributedString(string: "\n\(subtitle)", attributes: [.font : UIFont.systemFont(ofSize: subtitleSize)]))
label.attributedText = text
self.navigationItem.titleView = label
}
}
Custom titleView based in part on https://stackoverflow.com/a/34298491/3918865
Swift 4:
import UIKit
class NavigationTitleView: UIView {
private var contentStackView = UIStackView()
private var titleLabel = UILabel()
private var subTitleLabel = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
viewConfig()
addViewsConfig()
layoutViewsConfig()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func set(title: String, subTitle: String){
self.titleLabel.text = title
self.subTitleLabel.text = subTitle
}
private func viewConfig() {
contentStackView.axis = .vertical
contentStackView.alignment = .center
contentStackView.distribution = .fill
contentStackView.spacing = 5
self.backgroundColor = .clear
self.titleLabel.textColor = .white
self.self.subTitleLabel.textColor = .white
}
private func addViewsConfig() {
contentStackView.addArrangedSubview(subTitleLabel)
contentStackView.addArrangedSubview(titleLabel)
self.addSubview(contentStackView)
}
private func layoutViewsConfig(){
contentStackView.translatesAutoresizingMaskIntoConstraints = false
contentStackView.centerXAnchor.constraint(equalTo: self.centerXAnchor, constant: 0.0).isActive = true
contentStackView.centerYAnchor.constraint(equalTo: self.centerYAnchor, constant: 0.0).isActive = true
}
}
Use:
import UIKit
class ViewController: UIViewController {
private var navigationTitleView = NavigationTitleView()
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.titleView = navigationTitleView
navigationTitleView.set(title: "title", subTitle: "subTitle")
}
}
Easy fix for iOS 16. Following the #iosjillian's / #Dan's approx, calling layoutSubviews() on stack view does the trick.
extension UINavigationItem {
func setTitle(_ title: String, subtitle: String) {
let textColor = getTextColor()
let titleLabel = UILabel()
titleLabel.text = title
titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
titleLabel.textColor = textColor
let subtitleLabel = UILabel()
subtitleLabel.text = subtitle
subtitleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.subheadline)
subtitleLabel.textColor = textColor.withAlphaComponent(0.75)
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.distribution = .equalCentering
stackView.alignment = .center
stackView.axis = .vertical
stackView.layoutSubviews()
self.titleView = stackView
}
}
Working iOS 16 solution. Swift 5.7
With SnapKit library. If you are not using SnapKit lib, just make both views (titleLabel and subtitleLabel) translatesAutoresizingMaskIntoConstraints = false and replace SnapKit Constraints with native constraints.
func setTitle(title: String, subtitle: String, view: UIView) -> UIView {
let appearance = UINavigationBar.appearance()
let titleColor = appearance.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor ?? .black
let titleLabel = UILabel()
titleLabel.backgroundColor = UIColor.clear
titleLabel.textColor = titleColor
titleLabel.adjustsFontSizeToFitWidth = false
titleLabel.font = .preferredFont(forTextStyle: UIFont.TextStyle.headline)
titleLabel.lineBreakMode = .byTruncatingTail
titleLabel.textAlignment = .center
titleLabel.text = title
let subtitleLabel = UILabel()
subtitleLabel.backgroundColor = UIColor.clear
subtitleLabel.textColor = UIColor.init(hexString: "#808890")
subtitleLabel.adjustsFontSizeToFitWidth = false
subtitleLabel.lineBreakMode = .byTruncatingTail
subtitleLabel.textAlignment = .center
subtitleLabel.font = UIFont.systemFont(ofSize: 11)
subtitleLabel.text = subtitle
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 30))
titleView.addSubview(titleLabel)
titleView.addSubview(subtitleLabel)
titleLabel.snp.makeConstraints { make in
make.horizontalEdges.equalToSuperview()
make.top.equalToSuperview().offset(-20)
make.height.equalTo(20)
}
subtitleLabel.snp.makeConstraints { make in
make.horizontalEdges.equalToSuperview()
make.top.equalTo(titleLabel.snp.bottom).offset(4)
make.height.equalTo(10)
}
return titleView
}

swift - how adjust kerning in navigation bar text?

I'm not seeing anything here on text kerning for the navigation bar. Anyone know how to do this? I have set up the text in ViewDidLoad()
self.navigationController?.navigationBar.topItem?.title = "My Nav Title"
self.navigationController?.navigationBar.titleTextAttributes = [ NSFontAttributeName: UIFont(name: "Helvetica", size: 20)!, NSForegroundColorAttributeName: UIColor.whiteColor()]
I see we can adjust kerning but can't get this code to work
attributes: [NSKernAttributeName: 5.0]
Thank Much!!!
I found and modified this code from an earlier post and it works now in xcode 7.3.1
let titleLabel = UILabel()
let attributes: NSDictionary = [
NSFontAttributeName:UIFont(name: "Helvetica", size: 20)!,
NSForegroundColorAttributeName:UIColor.blackColor(),
NSKernAttributeName:CGFloat(8.0)
]
let attributedTitle = NSAttributedString(string: ""My Nav Title", attributes: attributes as? [String : AnyObject])
titleLabel.attributedText = attributedTitle
titleLabel.sizeToFit()
self.navigationItem.titleView = titleLabel