Why disappear navigationBar backButtonItem? Swift4 - swift

I use largeTitleBar.
When navigationBar.title has character over 20 bytes (in Case iPhone SE), backButtonItem.title disappears. (In case iPhoneX, 22 bytes)
navigationBar.title becomes truncate affects this problem.
But I don't have any idea to solve this problem.
navigationItem.title = someText
// when navigationItem.title is truncated, backButtonItem.title disappears.

self.title = "Your TiTle Text"
var lbl = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
lbl.text = navigationItem?.title
lbl.textColor = UIColor.white
if let fontSize = UIFont(name: "Helvetica-Bold", size: 30.0) {
lbl.font = fontSize
}
lbl.backgroundColor = UIColor.clear
lbl.adjustsFontSizeToFitWidth = true
lbl.textAlignment = .center
navigationItem?.titleView = lbl
Try above code may help you.

Related

How to calculate boundRect correctly for NSAttributedString When apply shadow

I tried apply shadow effect to UILabel using NSAttributeString. I want to change the size of UILabel whenever change shadow offset. However, it doesn't calculate correct frame and ignore shadow offset. How can I achieve?
let textView = UILabel(frame: CGRect(x: 20, y: 100, width: 300, height: 50))
textView.layer.borderColor = UIColor.red.cgColor
textView.layer.borderWidth = 1
textView.numberOfLines = 0
textView.text = "hyHgjpQ\nhyHgjpQ\nhyHgjpQ"
textView.font = UIFont(name: "DamascusSemiBold", size: 40)
textView.textColor = .white
textView.textAlignment = .center
self.view.addSubview(textView)
var textAttributes: [NSAttributedString.Key: AnyObject] = [:]
// font name & size
let font = UIFont(name: "HelveticaNeue", size: 50)
textAttributes[NSAttributedString.Key.font] = font
// shadow
let shadow = NSShadow()
shadow.shadowBlurRadius = 10
shadow.shadowOffset = CGSize(width: 10, height: 30)
shadow.shadowColor = UIColor.red
textAttributes[NSAttributedString.Key.shadow] = shadow
textView.attributedText = NSAttributedString(string: textView.text ?? "", attributes: textAttributes)
let bound = textView.text!.boundingRect(with: CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude),
options: .usesLineFragmentOrigin,
attributes: textAttributes,
context: nil)
textView.bounds = bound
Result

Add label in center of imageView

I have the two images like in the picture at the end of the question (the image of a list and a red dot). I want to add a label in the center of the red dot. This is my code that doesn't work:
image = UIImageView(image: UIImage(named: "pallino"))
image.frame = CGRect(x: 55, y: self.view.frame.height-60, width: 22, height: 22)
self.view.addSubview(image)
image.layer.cornerRadius = image.frame.width/2
label = UILabel(frame: CGRect(x: self.image.center.x, y: self.image.center.y, width: image.frame.size.width, height: image.frame.size.height))
label.text = "4"
label.font = UIFont(name:"HelveticaNeue-Bold", size: 15.0)
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
image.addSubview(label)
Can someone tell me were am I wrong?
Problem in this line:
label = UILabel(frame: CGRect(x: self.image.center.x, y: self.image.center.y, width: image.frame.size.width, height: image.frame.size.height))
self.image.center.x - The center point is specified in points in the coordinate system of its superview, it is mean that self.image.center is not center of image
You need frame for label, something like this:
let imageSize = 22
let frame = CGRect(x: 0, y: 0, width: imageSize, height: imageSize)
let label = UILabel(frame: frame)
label.aligment = .center
You can set constraint in your label(centered Horizontally and Vertically). Try with the following code.
let label = UILabel()
label.text = "4"
label.font = UIFont(name:"HelveticaNeue-Bold", size: 15.0)
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
image.addSubview(label)
label.centerXAnchor.constraint(equalTo: self.image.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: self.image.centerYAnchor).isActive = true
To center the label in UIImageView you can refer to this example which is tested and working solution.
extension UIImageView {
/// Create label programmatically
/// - Returns: UILabel
private func ageSensitiveLabel() -> UILabel {
let label = UILabel(frame: CGRect(x: 0, y: 0, width: self.bounds.width, height: self.bounds.height))
label.text = "Content"
label.font = .systemFont(ofSize: 5.0)
label.textAlignment = .center
label.numberOfLines = 0
label.minimumScaleFactor = 0.5
label.adjustsFontSizeToFitWidth = true
label.baselineAdjustment = .alignCenters
label.textColor = .white
return label
}
/// Add label as subview to UIImageView
func addAgeSensitiveLabel() {
self.subviews.forEach { view in
DispatchQueue.main.async {
view.removeFromSuperview()
}
}
self.addSubview(ageSensitiveLabel())
}
}
Use it with UIImageView
imageView.addAgeSensitiveLabel()

How to set button width based on text for dynamically created button in swift 3?

I created the dynamic buttons. I need to change the width of button based on tittle label text. Here is my code.
for i in 0..<holdingSize {
let button = UIButton(type: .custom)
if i == 0 {
frame = CGRect(x: 10, y: 5, width: 100, height: 30)
}else{
buttonY = buttonY + 110
frame = CGRect(x: buttonY, y: 5, width: 100, height: 30)
}
button.setTitle("\(arrayOfHoldings[i])", for: UIControlState.normal) // We are going to use the item name as the Button Title here.
button.titleLabel?.text = "\(arrayOfHoldings[i])"
button.titleLabel?.font = UIFont(name: (button.titleLabel?.font.fontName)!, size: 15)
button.setTitleColor(Colors.green, for: .normal)
button.sizeToFit()
}
Try this
func labelSizeWithString(text: String,fontSize: CGFloat, maxWidth : CGFloat,numberOfLines: Int) -> CGRect{
let font = UIFont.systemFontOfSize(fontSize)//(name: "HelveticaNeue", size: fontSize)!
let label = UILabel(frame: CGRectMake(0, 0, maxWidth, CGFloat.max))
label.numberOfLines = numberOfLines
label.font = font
label.text = text
label.sizeToFit()
return label.frame
}
This will give you the frame of the label, you can set the height of your button from that.
FOR SWIFT 3.0
func labelSize(for text: String,fontSize: CGFloat, maxWidth : CGFloat,numberOfLines: Int) -> CGRect{
let font = UIFont.systemFont(ofSize: fontSize)//(name: "HelveticaNeue", size: fontSize)!
let label = UILabel(frame: CGRect(x: 0, y: 0, width: maxWidth, height: CGFloat.leastNonzeroMagnitude))
label.numberOfLines = numberOfLines
label.font = font
label.text = text
label.sizeToFit()
return label.frame
}

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 UITextField icon position set

Add Icon in UITextField using
var leftImageView = UIImageView()
leftImageView.image = leftImage
textField.leftView = leftImageView
textField.leftViewMode = UITextFieldViewMode.Always
leftImageView.frame = CGRectMake(15, 10, 15, 20)
textField.addSubview(leftImageView)
o/p for this
I found solution like remove this code from above code
textField.leftView = leftImageView
It give icon alignment proper but whenever start editing text field text on icon like this
I want o/p like this
First of all, you should definitely not add the image as a subview to the label. It's enough to set the leftView property.
textField.addSubview(leftImageView) // Delete this line
Secondly, any x or y offsets that you apply to the left view's frame are ignored. The text field will only care about the view's size. If you want to add padding around the image, one option is to use a container view and position the image view inside of it.
let leftImageView = UIImageView()
leftImageView.image = leftImage
let leftView = UIView()
leftView.addSubview(leftImageView)
leftView.frame = CGRectMake(0, 0, 30, 20)
leftImageView.frame = CGRectMake(0, 0, 15, 20)
textField.leftView = leftView
Another option would be to subclass UITextField and override leftViewRectForBounds.
Referring #hennes syntax , there are some syntax missing in swift like CGRect syntax is changed and userNameTextField.leftViewMode = .always is missing
With swift syntax this worked for me :
let leftImageView = UIImageView()
leftImageView.image = UIImage(named: "email")
let leftView = UIView()
leftView.addSubview(leftImageView)
leftView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
leftImageView.frame = CGRect(x: 10, y: 10, width: 20, height: 20)
userNameTextField.leftViewMode = .always
userNameTextField.leftView = leftView
Try this. May be help you.
var padding: Float = 20
var envelopeView: UIImageView = UIImageView(frame: CGRectMake(padding, 0, 30, 30))
envelopeView.image = UIImage.imageNamed("comment-128.png")
envelopeView.contentMode = UIViewContentModeScaleAspectFit
var viewLeft: UIView = UIView(frame: CGRectMake(padding, 0, 30, 30))
viewLeft.addSubview(envelopeView)
textField.leftView.setFrame(envelopeView.frame)
textField.leftView = viewLeft
textField.leftViewMode = UITextFieldViewModeAlways
var viewRight: UIView = UIView(frame: CGRectMake(textField.frame.size.width - (textField.frame.size.width + 30 + padding), 0, 30, 30))
viewRight.addSubview(envelopeView)
textField.rightView.setFrame(envelopeView.frame)
textField.rightView = viewRight
textField.rightViewMode = UITextFieldViewModeAlways
Swift 3.1
extension UITextField
{
enum Direction
{
case Left
case Right
}
func AddImage(direction:Direction,imageName:String,Frame:CGRect,backgroundColor:UIColor)
{
let View = UIView(frame: Frame)
View.backgroundColor = backgroundColor
let imageView = UIImageView(frame: Frame)
imageView.contentMode = .center
imageView.image = UIImage(named: imageName)
View.addSubview(imageView)
if Direction.Left == direction
{
self.leftViewMode = .always
self.leftView = View
}
else
{
self.rightViewMode = .always
self.rightView = View
}
}
}