Swift UIKIT components do not show on a working view - swift

I'm turning my hand from years of Java / C++ / C to trying to learn Swift and how to develop on Apple.
I've tried a lot of tutorials and for what I want to achieve I need to use a splitview controller.
I need to have a few different detail views and can't work out how to do this via storyboard and thought I'd try to do it with code.
Following a tutorial and several google searches Im hitting a problem which I realise is me being a newbiee and ask for your help.
To keep things simple I created a test viewcontroller class and set it in the storyboard no problem.
import UIKit;
class tesctVC : UIViewController {
var scoreLabel: UILabel!
override func loadView() {
view = UIView()
view.backgroundColor = .white
scoreLabel = UILabel()
scoreLabel.translatesAutoresizingMaskIntoConstraints = false
scoreLabel.textAlignment = .right
scoreLabel.text = "Score: 0"
view.addSubview(scoreLabel)
// more code to come!
}
}
When I change to background color it shows as expected.
But when I try to add anything else, I've tried UILabels, UIButtons etc, they do not show.
Would you please give me some pointers?

Thanks again.
I thought this may help others.
This is still WIP but shows some hints I found along the way.
I want to add a bunch of labels & buttons etc.
This in so far as is written does the job. It needs more formatting and so on.
The extension is needed to set a stacks background color. I think someone explained its because a StackView is not a drawable object.
extension UIStackView {
func addBackground(color: UIColor) {
let subView = UIView(frame: bounds)
subView.backgroundColor = color
subView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
insertSubview(subView, at: 0)
}
}
class DetailViewController: UIViewController , UIWebViewDelegate {
var usernameEdt: UITextField!
var passwordEdt: UITextField!
var statusLabel: UILabel!
var LogubButton = UIButton()
// Draw the login page
func loginPage() {
// Create the top level statck
let stackView = UIStackView()
stackView.addBackground(color: .white)
stackView.axis = .vertical
stackView.alignment = .center // .leading .firstBaseline .center .trailing .lastBaseline
stackView.distribution = .fillEqually // .fillEqually .fillProportionally .equalSpacing .equalCentering
// Add the title
let label = UILabel()
label.frame = CGRect(x: 150, y: 200, width: 200, height: 20)
label.font = UIFont.boldSystemFont(ofSize: 20.0)
label.text = "Login Details"
label.textColor = .blue
stackView.addArrangedSubview(label)
// Add Username
usernameEdt = UITextField()
usernameEdt.placeholder = "Enter Your Username"
stackView.addArrangedSubview(usernameEdt)
// Add Password
passwordEdt = UITextField()
passwordEdt.placeholder = "Enter Your Password"
stackView.addArrangedSubview(passwordEdt)
self.view = stackView
}
Thanks
Jeff

Related

Is there a way to make a completely transparent UIButton that works?

I've a custom UINavigationItem title view. It has a label pushed to the top of it, which I've enabled to respond to .touchDown and send an action.
However, the taps don't register near the top of the label, presumably because the active region is clipped. So I configured another invisible view (not in navigation item), and set it up as a control, and positioned it above that navigation title view label.
However, it doesn't work unless I set the 'invisible' view's alpha to at least 0.02, because Apple seems to intentionally disable action for a view with an alpha less than that. Unfortunately, against a black screen in dark mode, the 'invisible' hitpad view shows up as a slightly grey rectangle, which is not a good aesthetic.
I guess I could go to some lengths to try to make the button background color match the screen background at that location, but it seems a bit tacky.
What alternatives might I have?
You can simply create a blank png image, and add it in top of your title view. make sure to set the imageView and the title view isUserInteractionEnabled properties to true:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
let imageView = UIImageView()
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(
target: self,
action: #selector(tap)
)
imageView.addGestureRecognizer(tap)
imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
imageView.image = UIImage(named: "blank")
let titleLabel = UILabel()
titleLabel.text = "Transparent Button"
titleLabel.autoresizingMask = [.flexibleHeight, .flexibleWidth]
titleLabel.addSubview(imageView)
navigationItem.titleView = titleLabel
navigationItem.titleView?.isUserInteractionEnabled = true
}
#objc func tap(_ gesture: UITapGestureRecognizer) {
print(#function)
}
}
Sample project
You can also just add your gesture recognizer directly to your titleView. No need for the transparent image at all unless you need to control the area of the gesture:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .green
let tap = UITapGestureRecognizer(
target: self,
action: #selector(tap)
)
let titleLabel = UILabel()
titleLabel.text = "Transparent Button"
titleLabel.autoresizingMask = [.flexibleHeight, .flexibleWidth]
titleLabel.addGestureRecognizer(tap)
navigationItem.titleView = titleLabel
navigationItem.titleView?.isUserInteractionEnabled = true
}
#objc func tap(_ gesture: UITapGestureRecognizer) {
print(#function)
}
}
This is an adaptation of the #LeoDabus' Accepted answer that works. However it was utterly informed by his explanation and example. The only meaningful change I made to Leo's example was to create a real empty image programmatically, and drop the label generation. Without a real empty UIImage(), the only way to make taps on the region work that I found is to set the image view's background color to non-clear.
func emptyImage(with size: CGSize) -> UIImage?
{
UIGraphicsBeginImageContext(size)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
func configureButtons() {
let imageView = UIImageView(image: emptyImage(with: CGSize(width: view.frame.size.width - 250, height: 44)))
imageView.frame = CGRect(x: 140, y: self.view.safeAreaInsets.top + 50,
width: view.frame.size.width - 250, height: 44)
imageView.isUserInteractionEnabled = true
let tap = UITapGestureRecognizer(target: self, action: #selector(actionEnableTitleEditing))
imageView.addGestureRecognizer(tap)
imageView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(imageView)
}

Can we acces a UIStackView's subview's size?

My question is pretty simple but I haven't found any answer yet.
I am making a sort of table using two vertical stacks inside a horizontal stack. Both vStacks have different objects (Button in my case, with border for each one) but in the same quantity (so that they are horizontally paired like in a classic table).
I have set both of my vStack's distribution to .fillProportionally, and therefore each button have different size depending on their titleLabel length.
However, I would like to make each of my button have the same size of its paired button (the one next to him horizontally, in the other vStack) so that my cells borders would be aligned (using the biggest button's size as a reference in each pair).
I think it involves to find a way to access one stack's subview's size and then constraint the other stack subview to be equally sized. Or, because usually there is only one big button messing with the distribution and offsetting button pairs' border, accessing the way one stack displays its subviews and forcing the other stack to adopt the same way. Either way, I don't know how to do it yet.
I'd be glad if you could help me or lead me to the answer !
(I don't think I need to put code to explain my problem as it's a relatively abstract issue but if you need it I can share it)
EDIT :
Left : What I want, right : What I get
Each cell is a button (useless here but in my app it will have a functionality) with border , I want to set "description" button's height equal as "text" button. I hope it's clearer now :) I tried to invert the layout (two horizontal stacks in one vertical stack) but the issue is still here, with width instead of height this time.
EDIT 2 :
Following your advice, here is some code :
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var bottomButton: UIButton!
#IBOutlet weak var vstack: UIStackView!
#objc func buttonAction(sender: UIButton) {
sender.isEnabled = true
print(sender.frame)
}
override func viewDidLoad() {
super.viewDidLoad()
let button = newButton(text: "name")
let button2 = newButton(text: "John Smith")
let button3 = newButton(text: "Description")
let button4 = newButton(text: "text text text text text text text text text text text \n text text text text text \n text text text text text")
let hStack = UIStackView()
hStack.axis = .horizontal
hStack.distribution = .fillEqually
let hStack2 = UIStackView()
hStack2.axis = .horizontal
hStack2.distribution = .fillEqually
hStack.addArrangedSubview(button)
hStack.addArrangedSubview(button2)
hStack2.addArrangedSubview(button3)
hStack2.addArrangedSubview(button4)
vstack.addArrangedSubview(hStack)
vstack.addArrangedSubview(hStack2)
}
}
func newButton(text: String) -> UIButton {
let button = UIButton(type: .system)
button.isEnabled = true
button.setTitle(text, for: .disabled)
button.setTitle(text, for: .normal)
button.setTitleColor(.black, for: .disabled)
button.layer.borderWidth = 1
button.titleLabel?.numberOfLines = 0
button.titleLabel?.textAlignment = NSTextAlignment.center
return button
}
`
Using horizontal stacks in a vertical stack and Fill Equally partially solves the problem, because it only works when my text is under a certain length, otherwise it clips (see following image), which is why I was using fillProportionally.
OK - part of the problem is that you are modifying the titleLabel properties -- specifically, setting its .numberOfLines = 0. Auto-layout does not take that into account, without a little help.
You'll want to use a button subclass, such as this:
class MultiLineButton: UIButton {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
func commonInit() -> Void {
titleLabel?.numberOfLines = 0
titleLabel?.textAlignment = .center
// if you want to give your buttons some "padding" around the title
//contentEdgeInsets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0)
}
override var intrinsicContentSize: CGSize {
guard let tl = titleLabel else {
return .zero
}
let size = tl.intrinsicContentSize
return CGSize(width: size.width + contentEdgeInsets.left + contentEdgeInsets.right, height: size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}
override func layoutSubviews() {
super.layoutSubviews()
guard let tl = titleLabel else { return }
tl.preferredMaxLayoutWidth = tl.frame.size.width
}
}
Using that class, here is an example view controller where we add a vertical stack view, and 2 horizontal "row" stack views:
class PruViewController: UIViewController {
func newButton(text: String) -> MultiLineButton {
let b = MultiLineButton()
b.titleLabel?.font = .systemFont(ofSize: 15.0)
b.setTitle(text, for: [])
b.setTitleColor(.blue, for: .normal)
b.setTitleColor(.lightGray, for: .highlighted)
b.setTitleColor(.black, for: .disabled)
b.layer.borderWidth = 1
b.layer.borderColor = UIColor.black.cgColor
return b
}
override func viewDidLoad() {
super.viewDidLoad()
let button = newButton(text: "name")
let button2 = newButton(text: "John Smith")
let button3 = newButton(text: "Description")
let button4 = newButton(text: "text text text text text text text text text text text \n text text text text text \n text text text text text")
let vStack = UIStackView()
vStack.axis = .vertical
vStack.distribution = .fill
let hStack = UIStackView()
hStack.axis = .horizontal
hStack.distribution = .fillEqually
let hStack2 = UIStackView()
hStack2.axis = .horizontal
hStack2.distribution = .fillEqually
hStack.addArrangedSubview(button)
hStack.addArrangedSubview(button2)
hStack2.addArrangedSubview(button3)
hStack2.addArrangedSubview(button4)
vStack.addArrangedSubview(hStack)
vStack.addArrangedSubview(hStack2)
vStack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(vStack)
// respect safe area
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
vStack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
vStack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
vStack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
])
}
}
The result:
And, you'll notice in my MultiLineButton class a comment about adding "padding" around the button title labels... here's how it looks with that line un-commented:

How can I center a button on view programmatically

I am trying to center a Button onto the bottom of a view but it never appears. The only time it appears is when I uncomment takePhotoButton.frame. What is the proper way to do this?
import UIKit
import AVFoundation
class InputViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let photoPreviewImageView = UIImageView()
photoPreviewImageView.frame = view.bounds
photoPreviewImageView.backgroundColor = UIColor.green
view.addSubview(photoPreviewImageView)
let imageOfPhotoButton = UIImage(named: "smallcircle.circle.fill") as UIImage?
let takePhotoButton = UIButton(type: .custom) as UIButton
takePhotoButton.setImage(imageOfPhotoButton, for: .normal)
//takePhotoButton.frame = CGRect(x: 10, y: 10, width: 60, height: 60) // It will appear with this code however i took it away because im trying to center it at the bottom of the screen
takePhotoButton.center = view.center
photoPreviewImageView.addSubview(takePhotoButton)
}
}
Use constraint anchors. After you add the takePhotoButton set them the following way:
takePhotoButton.bottomAnchor.constraint(equalTo: photoPreviewImageView.bottomAnchor).isActive = true
takePhotoButton.centerXAnchor.constraint(equalTo: photoPreviewImageView.centerXAnchor).isActive = true
This will set make your button have the same bottom and center as it's container.
Good day,
you have to add constraint.
import UIKit
class ViewController: UIViewController {
var loginButton : UIButton = {
let button = UIButton(type: .system)
button.setTitle("Login", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .red
button.tintColor = .white
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
constraintsInit()
}
func constraintsInit(){
view.addSubview(loginButton)
NSLayoutConstraint.activate([
loginButton.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
loginButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
loginButton.heightAnchor.constraint(equalToConstant: 30),
loginButton.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,constant: 30),
loginButton.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,constant: -30),
])
}
}
on youtube you can find several people that explain how create the views, using only code.

Nested Stack Views: Child Stack View is not inside its parent stack view when attaching it programmatically

I'm trying to implement nested stack views in which one stack view is inside another stack view. I've based my code here. My current code is here below.
#IBOutlet weak var verticalStackView: UIStackView!
let blueImageView = UIImageView()
blueImageView.backgroundColor = UIColor.blueColor()
blueImageView.image = UIImage(named: "just some image")
blueImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let greenImageView = UIImageView()
greenImageView.backgroundColor = UIColor.greenColor()
greenImageView.image = UIImage(named: "just some image")
// This is just from SnapKit
greenImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let stackView = UIStackView()
stackView.axis = UILayoutConstraintAxis.Horizontal
stackView.distribution = UIStackViewDistribution.EqualSpacing
stackView.alignment = UIStackViewAlignment.Center
stackView.spacing = 16.0
stackView.addArrangedSubview(blueImageView)
stackView.addArrangedSubview(greenImageView)
stackView.translatesAutoresizingMaskIntoConstraints = false;
// This is just from SnapKit
verticalStackView.snp_makeConstraints { (make) in
make.height.equalTo(70)
}
verticalStackView.addSubview(stackView)
When I tried running it looks this way.
As you can see, the sub stack view stackView is below the parent stack view (verticalStackView) in the hierarchy. But the positioning is off.
I'm quite new with Swift, AutoLayout and StackViews. Anyone that can help point out what I'm missing here?
Thanks!
So I've figured out the solution after some reading..
#IBOutlet weak var verticalStackView: UIStackView!
let blueImageView = UIImageView()
blueImageView.backgroundColor = UIColor.blueColor()
blueImageView.image = UIImage(named: "buttonFollowCheckGreen")
blueImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let greenImageView = UIImageView()
greenImageView.backgroundColor = UIColor.greenColor()
greenImageView.image = UIImage(named: "buttonFollowCheckGreen")
greenImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
let firstLineStackView = UIStackView()
firstLineStackView.axis = UILayoutConstraintAxis.Horizontal
firstLineStackView.distribution = UIStackViewDistribution.Fill
firstLineStackView.alignment = UIStackViewAlignment.Center
firstLineStackView.spacing = 8.0
firstLineStackView.addArrangedSubview(blueImageView)
firstLineStackView.addArrangedSubview(greenImageView)
firstLineStackView.translatesAutoresizingMaskIntoConstraints = false;
let redImageView = UIImageView()
redImageView.backgroundColor = UIColor.redColor()
redImageView.image = UIImage(named: "buttonFollowCheckGreen")
redImageView.snp_makeConstraints { (make) in
make.height.width.equalTo(34)
}
verticalStackView.addArrangedSubview(firstLineStackView)
I just had to use addArrangedSubview instead of addSubview and let Auto layout do the rest. Just to fix the positioning and size of the image views, I've also modified the alignment of the verticalStackView from fill to leading.
greenImageView.heightAnchor.constraint(equalToConstant: 34).isActive = true
greenImageView.widthAnchor.constraint(equalToConstant: 34).isActive = true
and it will be solved

How to resize Title in a navigation bar dynamically

I have some views that show up in a navigation controller. Two of these views have a longer title for the navigation bar.
The problem is that when the title is too long to fit, some characters are truncated and "..." is added.
Is there any way I can tell the Navigation bar to re-size the title text automatically to fit?
Used the below code in ViewDidload .
Objective C
self.title = #"Your TiTle Text";
UILabel* tlabel=[[UILabel alloc] initWithFrame:CGRectMake(0,0, 200, 40)];
tlabel.text=self.navigationItem.title;
tlabel.textColor=[UIColor whiteColor];
tlabel.font = [UIFont fontWithName:#"Helvetica-Bold" size: 30.0];
tlabel.backgroundColor =[UIColor clearColor];
tlabel.adjustsFontSizeToFitWidth=YES;
tlabel.textAlignment = NSTextAlignmentCenter;
self.navigationItem.titleView=tlabel;
Swift Version
self.title = "Your Title Text"
let tlabel = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
tlabel.text = self.title
tlabel.textColor = UIColor.white
tlabel.font = UIFont.systemFont(ofSize: 30, weight: .bold)
tlabel.backgroundColor = UIColor.clear
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .center
self.navigationItem.titleView = tlabel
Hope it works for you.Thanks
Swift version of Accepted Answer + putting the label text on center :
Swift 2.3:
self.title = "Your TiTle Text"
let tlabel = UILabel(frame: CGRectMake(0, 0, 200, 40))
tlabel.text = self.title
tlabel.textColor = UIColor.whiteColor()
tlabel.font = UIFont.boldSystemFontOfSize(17) //UIFont(name: "Helvetica", size: 17.0)
tlabel.backgroundColor = UIColor.clearColor()
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .Center
self.navigationItem.titleView = tlabel
And Swift 3 :
self.title = "Your TiTle Text"
let frame = CGRect(x: 0, y: 0, width: 200, height: 40)
let tlabel = UILabel(frame: frame)
tlabel.text = self.title
tlabel.textColor = UIColor.white
tlabel.font = UIFont.boldSystemFont(ofSize: 17) //UIFont(name: "Helvetica", size: 17.0)
tlabel.backgroundColor = UIColor.clear
tlabel.adjustsFontSizeToFitWidth = true
tlabel.textAlignment = .center
self.navigationItem.titleView = tlabel
This works for me
Objective C
[UILabel appearanceWhenContainedInInstancesOfClasses:#[[UINavigationBar class]]].adjustsFontSizeToFitWidth = YES;
Swift Version
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true
In case you have a view added into titleView, and you want to resize the view, you can use this code (Swift 3):
self.translatesAutoresizingMaskIntoConstraints = false
self.layoutIfNeeded()
self.sizeToFit()
self.translatesAutoresizingMaskIntoConstraints = true
None of the above solutions seam to work reliably for me.
However I found a solution by using different elements of the provides answers, its in Swift 2 and is really elegant as it does not require any custom code each time you change the label, it just uses property observers on the title.
Note that in my case, I had a back button on the left side of the navigation bar, which putted the text out of the center of the screen, to fix this I am using attributed text and the tailIndent. All comments/info in the code below :
class VCHowToTopic : UIViewController {
//add handlers so that any manipulation of the title is caught and transferred to the custom drawn UILabel
override var title : String? {
set {
super.title = newValue
configureTitleView()
}
get {
return super.title
}
}
//MARK: - lifecycle
func configureTitleView() {
//some large number that makes the navigationbar schrink down our view when added
let someVeryLargeNumber = CGFloat(4096)
//create our label
let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: someVeryLargeNumber, height: someVeryLargeNumber))
//0 means unlimited number of lines
titleLabel.numberOfLines = 0
//define style of the text (we will be using attributed text)
let style = NSMutableParagraphStyle()
style.alignment = .Center
//top compensate for the backbutton which moves the centered text to the right side of the screen
//we introduce a negative tail indent, the number of 56 has been experimentally defined and might
//depend on the size of your custom back button (if you have one), mine is 22x22 px
style.tailIndent = -56
//create attributed text also with the right color
let attrText = NSAttributedString(string: title!, attributes: [NSParagraphStyleAttributeName : style,
NSForegroundColorAttributeName : UIColor.whiteColor()])
//configure the label to use the attributed text
titleLabel.attributedText = attrText
//add it as the titleview
navigationItem.titleView = titleLabel
}
}
You can create a UILabel as UINavigationItem's titleView and set it's adjustsFontSizeToFitWidth to true.
class MyViewController: UIViewController {
override var title: String? {
didSet {
(self.navigationItem.titleView as? UILabel)?.text = self.title
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.titleView = UILabel().apply {
$0.font = .boldSystemFont(ofSize: 18)
$0.minimumScaleFactor = 0.5
$0.adjustsFontSizeToFitWidth = true
$0.text = self.title
}
}
}
Easy to use:
myViewController.title = "This is a long title, but don’t worry."
The apply closure in the above code is a trick, in order to make the programming experience better. There is also a with closure. Recommend to everyone.
protocol ScopeFunc {}
extension ScopeFunc {
#inline(__always) func apply(_ block: (Self) -> ()) -> Self {
block(self)
return self
}
#inline(__always) func with<R>(_ block: (Self) -> R) -> R {
return block(self)
}
}
extension NSObject: ScopeFunc {}
Swift 5 and iOS 13 / iOS 14
The answers from above don't work if you have a large title in Swift 5 and iOS 13 because they simply add another title to your navigation bar. Instead you could use the largeTitleTextAttributes property (available since iOS 11) to shrink your title when needed.
Assuming you have set your large title via storyboard or code already, you can use the following method:
private func configureNavigationTitle(_ title: String) {
let tempLabel = UILabel()
tempLabel.font = UIFont.systemFont(ofSize: 34, weight: .bold)
tempLabel.text = title
if tempLabel.intrinsicContentSize.width > UIScreen.main.bounds.width - 30 {
var currentTextSize: CGFloat = 34
for _ in 1 ... 34 {
currentTextSize -= 1
tempLabel.font = UIFont.systemFont(ofSize: currentTextSize, weight: .bold)
if tempLabel.intrinsicContentSize.width < UIScreen.main.bounds.width - 30 {
break
}
}
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font : UIFont.systemFont(ofSize: currentTextSize, weight: .bold)]
}
self.title = title
}
So essentially we are ussing a helper label in order to get the width of our title and then we are going to shrink the font size until the title fits in our navigation bar.
Call it from viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad(
configureNavigationTitle("A very long title which fits perfectly fine")
}
you need to customize the navigation bar title view with uilabel and provide adjust font size..
[self.navigationItem setTitleView:<"Include any UI View subclass">];
Just calling sizeToFit() on my view after the change worked for me
Here's an example in Swift that also allows for multiple lines. Using PureLayout to simplify auto layout.
override func viewDidLoad() {
super.viewDidLoad()
configureTitleView()
}
func configureTitleView() {
let titleLabel = UILabel()
titleLabel.numberOfLines = 0
titleLabel.textAlignment = .Center
titleLabel.font = UIFont.boldSystemFontOfSize(17.0)
titleLabel.text = searchLoc.mapItem.name
navigationItem.titleView = titleLabel
titleLabel.autoPinEdgesToSuperviewMargins() // PureLayout method
titleLabel.adjustsFontSizeToFitWidth = true
}
And a usage example:
Swift 4 and iOS 13
Adding this so my future self can find it. Views added to titleView for some reason don't like to automatically resize themselves. So you have to do it manually.
Example
(navigationItem.titleView as? UILabel)?.text = "A longer string..." // label not resized and text is cut off
Solution
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = false
navigationItem.titleView?.setNeedsLayout()
navigationItem.titleView?.layoutIfNeeded()
navigationItem.titleView?.translatesAutoresizingMaskIntoConstraints = true
Thanks to #Paolo Musolino for leading me here.