Align Drop Down Menu Title to prevent overlapping of other buttons - swift

I have the below image where the title of the drop down menu title is overlapped with other buttons. How to solve this issue?
Code:
func createDropDownMenu() {
// create the drop down menu
let title = prepareNavigationBarMenuTitleView()
prepareNavigationBarMenu(title)
updateMenuContentOffsets()
}
func prepareNavigationBarMenuTitleView() -> String {
// Both title label and image view are fixed horizontally inside title
// view, UIKit is responsible to center title view in the navigation bar.
// We want to ensure the space between title and image remains constant,
// even when title view is moved to remain centered (but never resized).
titleView = DropDownTitleView(frame: CGRect(x: 0, y: 0, width: 150, height: 40))
titleView.addTarget(self,
action: #selector(DocumentViewController.willToggleNavigationBarMenu(_:)),
for: .touchUpInside)
titleView.addTarget(self,
action: #selector(DocumentViewController.didToggleNavigationBarMenu(_:)),
for: .valueChanged)
titleView.titleLabel.lineBreakMode = NSLineBreakMode.byClipping
titleView.titleLabel.numberOfLines = 2
titleView.titleLabel.textColor = UIColor.black
titleView.title = currentNode.title
navigationItem.titleView = titleView
return titleView.title!
}

I had to set the frame of the TitleLable and set the numberOfLines = 0 to solve my problem.
Code:
titleView.titleLabel.frame = CGRect(x: 0, y: 0, width: 600, height: 80)
titleView.numberOfLines = 0
titleView.titleLabel.text = currentNode.title

Related

adjustsFontSizeToFitWidth not working with iOS 15 UIButton

I want to size the text on my button to automatically fit to the edges of the button.
This code was working pre-iOS15, now it doesn't.
testButton.titleLabel?.adjustsFontSizeToFitWidth = true
testButton.titleLabel?.minimumScaleFactor = 0.1
How can I automatically resize the text to fit the size of my button in iOS 15?
This is playground code for testing. It includes everything I've tried, but still isn't working.
import UIKit
import PlaygroundSupport
//building button
let testButton = UIButton(frame: CGRect(x: 100, y: 100, width: 300, height: 300))
testButton.setTitle("test", for: .normal)
//everything I've found in other answers that is supposed to work
var titleLabel = testButton.titleLabel!
titleLabel.font = UIFont.systemFont(ofSize: 300)
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.minimumScaleFactor = 0.1
titleLabel.numberOfLines = 1 //also tried 0 instead
titleLabel.lineBreakMode = .byClipping
//configuring button
testButton.configuration = UIButton.Configuration.filled()
//display the button
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
containerView.backgroundColor = .white
PlaygroundPage.current.liveView = containerView
containerView.addSubview(testButton)
Related questions I've already looked at and tried:
Best way to adjust font size with width and height of UILabel
Swift - Adjusting fontSize to fit the width of the layout (programmatically)
How to get .adjustsFontSizeToFitWidth to function properly
How to set font size to fill UILabel height?
Auto change the font size to fit the button in swift
UIButton auto-adjust Button font size Swift
I found out it could be caused by testButton.configuration = UIButton.Configuration.filled(), but I don't know why.

Centering buttons programatically in Swift

I am trying to center Google Sign In and Sign Out buttons programmatically. In order to put both of them in the third quarter. I create 2 views that I wanted to put buttons to their center, in Storyboard.
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()
let gSignIn = GIDSignInButton(frame: CGRect(x: 0, y: 0, width: loginView.frame.size.width, height: loginView.frame.size.height))
loginView.addSubview(gSignIn)
gSignIn.center = loginView.center
let gSignOut = UIButton(frame: CGRect(x: 0, y: 0, width: signOutView.frame.size.width, height: signOutView.frame.size.height))
gSignOut.backgroundColor = UIColor.white.withAlphaComponent(0)
gSignOut.setTitle("Sign Out", for: .normal)
gSignOut.setTitleColor(UIColor.red, for: .normal)
gSignOut.addTarget(self, action: #selector(self.signOut(_:)), for: .touchUpInside)
gSignOut.center = signOutView.center
self.signOutView.addSubview(gSignOut)
As you can see, also I am trying to resize buttons according to view size which helps me to resize buttons depend on device size.
Here is the second half of my storyboard.
Here is the simulator screen when I run the code.
Thanks.
Use auto-layout instead of setting frame.center, it will be more flexible.
Autolayout goes like below code.
GIDSignIn.sharedInstance()?.presentingViewController = self
GIDSignIn.sharedInstance().signIn()
let gSignIn = GIDSignInButton(frame: CGRect(x: 0, y: 0, width: loginView.frame.size.width, height: loginView.frame.size.height))
gSignIn.translatesautoresizingmaskintoconstraints = false
loginView.addSubview(gSignIn)
NSLayoutConstraint.activate([
gSignIn.leftAnchor.constraint(equalTo: loginView.leftAnchor),
gSignIn.rightAnchor.constraint(equalTo: loginView.rightAnchor),
gSignIn.topAnchor.constraint(equalTo: loginView.topAnchor),
gSignIn.bottomAnchor.constraint(equalTo: loginView.bottomAnchor)
])

How to show all TextField in a view that stored in a array / Swift 5

I have a tapGesture, that every time when you click on it a new TextField displays and will saved in a array.
I can create multiple TextField and can store them in a Array, but I have no idea, how I can display them on a view.
At the Moment only the first one will be shown in the view.
I want to build something similar like snapchat with the text.
var myTextField: UITextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
func addTapGestureToTextImageView() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTextImage))
textImageView.isUserInteractionEnabled = true
textImageView.addGestureRecognizer(tapGesture)
}
#objc func handleTextImage() {
textFields.append(myTextField)
let myTextField = UITextField(frame: CGRect(x: 0,y: 0, width: 300.0, height:30.0))
for i in 0..<textFields.count {
myTextField.tag = i
view.addSubview(textFields[i])
print(textFields[i])
}
print(textFields.count)
}
If you want to pile them vertically you have to increment the y with the height for each textField and hence change it's frame.
for i in 0..<textFields.count {
var field = textFields[i]
field.frame = CGRect(x: 0, y: 30*i, width: 300, height: 30)
view.addSubview(field)
print(textFields[i])
}
It will look something like this. But you will need to tweak it to properly align it.
My suggestion would be to put a UIStackView in the baseView and then use it's addArrangedSubview() and it will do the stacking for you.
Use stackView for this:
lazy var textFieldsView: UIStackView = {
let stackView = UIStackView()
stackView.spacing = 4
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.frame.size.width = 300
view.addSubview(stackView)
return stackView
}()
#objc func handleTextImage() {
let myTextField = UITextField(frame: CGRect(x: 0, y: 0, width: 300.0, height: 30.0))
for pair in textFields.enumerated() {
pair.element.tag = pair.offset
textFieldsView.addArrangedSubview(pair.element)
}
// Update stackView frame if needed
}

Swift: SegmentedControl in NavBar with Small TitleView

I am attempting to include a segmentedControl on my navBar that looks like this:
The idea here is that the text "fetching..." is a small titleView. However, my current implementation would result in the text "fetching..." on the lower side like so:
I implement large titles so that I can get two "rows" on the navBar, else the word "fetching..." will be hidden behind the segmentedControl.
Code:
let segmentedControl: UISegmentedControl = {
let items = ["Now","In 15 mins", "In 1 hour"]
let sc = UISegmentedControl(items: items)
sc.selectedSegmentIndex = 0
return sc
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.backBarButtonItem?.title = "Back"
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.titleView = segmentedControl
}
Does anyone have any advice?
You can create a customView that holds all the views you want to show in the navigation bar and set that view as titleView like below,
let segmentedControl: UISegmentedControl = {
let items = ["Now","In 15 mins", "In 1 hour"]
let sc = UISegmentedControl(items: items)
sc.selectedSegmentIndex = 0
return sc
}()
let fetchingLabel: UILabel = {
let label = UILabel(frame: .zero)
label.text = "Fetching..."
return label
}()
In viewDidLoad
let customView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 250))
customView.addSubview(segmentedControl)
customView.addSubview(fetchingLabel)
fetchingLabel.frame = CGRect(x: 150, y: 0, width: self.view.frame.width, height: 60)
segmentedControl.frame = CGRect(x: 60, y: 50, width: self.view.frame.width * 0.75, height: 30)
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .automatic
navigationItem.titleView = customView
This should give you below result. You can play with the values to do what you want.

iOS - add image and text in title of Navigation bar

I would like to create a nav bar similar to what's in the image that's attached.
The title of the nav bar will be a combination of an image and text.
Should this be done per any best practice?
How can it be done?
As this answer shows, the easiest solution is to add the text to your image and add that image to the navigation bar like so:
var image = UIImage(named: "logo.png")
self.navigationItem.titleView = UIImageView(image: image)
But if you have to add text and an image separately (for example, in the case of localization), you can set your navigation bar's title view to contain both image and text by adding them to a UIView and setting the navigationItem's title view to that UIView, for example (assuming the navigation bar is part of a navigation controller):
// Only execute the code if there's a navigation controller
if self.navigationController == nil {
return
}
// Create a navView to add to the navigation bar
let navView = UIView()
// Create the label
let label = UILabel()
label.text = "Text"
label.sizeToFit()
label.center = navView.center
label.textAlignment = NSTextAlignment.Center
// Create the image view
let image = UIImageView()
image.image = UIImage(named: "Image.png")
// To maintain the image's aspect ratio:
let imageAspect = image.image!.size.width/image.image!.size.height
// Setting the image frame so that it's immediately before the text:
image.frame = CGRect(x: label.frame.origin.x-label.frame.size.height*imageAspect, y: label.frame.origin.y, width: label.frame.size.height*imageAspect, height: label.frame.size.height)
image.contentMode = UIViewContentMode.ScaleAspectFit
// Add both the label and image view to the navView
navView.addSubview(label)
navView.addSubview(image)
// Set the navigation bar's navigation item's titleView to the navView
self.navigationItem.titleView = navView
// Set the navView's frame to fit within the titleView
navView.sizeToFit()
Use horizontal UIStackView should be much cleaner and easier
Please add the next extension to UIViewController
extension UIViewController {
func setTitle(_ title: String, andImage image: UIImage) {
let titleLbl = UILabel()
titleLbl.text = title
titleLbl.textColor = UIColor.white
titleLbl.font = UIFont.systemFont(ofSize: 20.0, weight: .bold)
let imageView = UIImageView(image: image)
let titleView = UIStackView(arrangedSubviews: [imageView, titleLbl])
titleView.axis = .horizontal
titleView.spacing = 10.0
navigationItem.titleView = titleView
}
}
then use it inside your viewController:
setTitle("yourTitle", andImage: UIImage(named: "yourImage"))
(this will align the text and the icon together to the center, if you want the text to be centered and the icon in the left, just add an empty UIView with width constraint equal to the icon width)
here is my 2 cents for Swift 4, since accepted answer didn't work for me (was mostly off the screen):
// .. in ViewController
var navBar = CustomTitleView()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// =================== navBar =====================
navBar.loadWith(title: "Budget Overview", leftImage: Images.pie_chart)
self.navigationItem.titleView = navBar
}
class CustomTitleView: UIView
{
var title_label = CustomLabel()
var left_imageView = UIImageView()
override init(frame: CGRect){
super.init(frame: frame)
setup()
}
required init?(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
setup()
}
func setup(){
self.addSubview(title_label)
self.addSubview(left_imageView)
}
func loadWith(title: String, leftImage: UIImage?)
{
//self.backgroundColor = .yellow
// =================== title_label ==================
//title_label.backgroundColor = .blue
title_label.text = title
title_label.font = UIFont.systemFont(ofSize: FontManager.fontSize + 5)
// =================== imageView ===================
left_imageView.image = leftImage
setupFrames()
}
func setupFrames()
{
let height: CGFloat = Navigation.topViewController()?.navigationController?.navigationBar.frame.height ?? 44
let image_size: CGFloat = height * 0.8
left_imageView.frame = CGRect(x: 0,
y: (height - image_size) / 2,
width: (left_imageView.image == nil) ? 0 : image_size,
height: image_size)
let titleWidth: CGFloat = title_label.intrinsicContentSize.width + 10
title_label.frame = CGRect(x: left_imageView.frame.maxX + 5,
y: 0,
width: titleWidth,
height: height)
contentWidth = Int(left_imageView.frame.width)
self.frame = CGRect(x: 0, y: 0, width: CGFloat(contentWidth), height: height)
}
var contentWidth: Int = 0 //if its CGFloat, it infinitely calls layoutSubviews(), changing franction of a width
override func layoutSubviews() {
super.layoutSubviews()
self.frame.size.width = CGFloat(contentWidth)
}
}
Swift 4.2 + Interface Builder Solution
As a follow-on to Lyndsey Scott's answer, you can also create a UIView .xib in Interface Builder, use that to lay out your title and image, and then update it on-the-fly via an #IBOutlet. This is useful for dynamic content, internationalization, maintainability etc.
Create a UIView subclass with a UILabel outlet and assign your new .xib to this class:
import UIKit
class FolderTitleView: UIView {
#IBOutlet weak var title : UILabel!
/// Create an instance of the class from its .xib
class func instanceFromNib() -> FolderTitleView {
return UINib(nibName: "FolderTitleView", bundle: nil).instantiate(withOwner: nil, options: nil)[0] as! FolderTitleView
}
}
Connect the label to your outlet (title in my example) in your .xib, then in your UIViewController:
/// Reference to the title view
var folderTitleView : FolderTitleView?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Set the screen title to match the active folder
updateTitle()
}
/// Updates the title of the navigation controller.
func updateTitle() {
self.title = ""
if folderTitleView == nil {
folderTitleView = FolderTitleView.instanceFromNib()
self.navigationItem.titleView = folderTitleView
}
folderTitleView!.title.text = "Listening"
folderTitleView!.layoutIfNeeded()
}
This results in a nice self-centering title bar with an embedded image that you can easily update from code.
// worked for me
create a view and set the frame
now add the image in the view and set the frame
after adding the image, add the label in same view and set the frame
after adding the image and label to view, add same view to navigationItem
let navigationView = UIView(frame: CGRect(x: 0, y: 0, width: 50 , height: 55))
let labell : UILabel = UILabel(frame: CGRect(x: -38, y: 25, width: 150, height: 25))
labell.text = "Your text"
labell.textColor = UIColor.black
labell.font = UIFont.boldSystemFont(ofSize: 10)
navigationView.addSubview(labell)
let image : UIImage = UIImage(named: ValidationMessage.headerLogoName)!
let imageView = UIImageView(frame: CGRect(x: -20, y: 0, width: 100, height: 30))
imageView.contentMode = .scaleAspectFit
imageView.image = image
//navigationItem.titleView = imageView
navigationView.addSubview(imageView)
navigationItem.titleView = navigationView