Set different font as per title and subtitle in UIlabel IBDesignable - ios

I had created the custom class for uilabel with below code:
#IBDesignable
public class CustomUILabel: UILabel {
public override func awakeFromNib() {
super.awakeFromNib()
configureLabel()
}
public override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
configureLabel()
}
func configureLabel() {
textColor = .white
font = UIFont(name: Constants.regularFontName, size: 14)
}
}
This help me to set the font through out the application.But i wanted to create different bold font type for title and regular font for subtitle.
Is this possible only with one file ?
Or i need to create different Extension for that type of UIlabel

You could for example add a custom style property like this:
#IBDesignable
public class CustomUILabel: UILabel {
enum CustomUILabelStyle {
case title, subtitle
var font: UIFont? {
switch self {
case .title:
return UIFont(name: Constants.boldFontName, size: 14)
case .subtitle:
return UIFont(name: Constants.regularFontName, size: 14)
}
}
var textColor: UIColor {
switch self {
// add cases if you want different colors for different styles
default: return .white
}
}
}
var style: CustomUILabelStyle = .title {
didSet {
// update the label's properties after changing the style
if style != oldValue {
configureLabel()
}
}
}
public override func awakeFromNib() {
super.awakeFromNib()
configureLabel()
}
public override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
configureLabel()
}
func configureLabel() {
font = style.font
textColor = style.textColor
}
}
You use it like this:
let label = CustomUILabel()
label.style = .title
// label.style = .subtitle

Related

Changing Search Bar placeholder text colour in iOS 13

I'm trying to set colour for placeholder text inside UISearchbar. Right now I've following code. It doesn't set white colour to placeholder text on iOS 13. It works on iOS 12. It seems something is either broken or support has been removed in iOS 13?
I've searched a lot and tried few workarounds but doesn't work. I've also tried to set attributed text colour for textfield but that also doesn't change colour.
Is there a working solution for this?
class CustomSearchBar: UISearchBar {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self.sizeToFit()
// Search text colour change
let textFieldInsideSearchBar = self.value(forKey: "searchField") as? UITextField
textFieldInsideSearchBar?.textColor = UIColor.white
// Search Placeholder text colour change
let placeHolderText = textFieldInsideSearchBar!.value(forKey: "placeholderLabel") as? UILabel
placeHolderText?.textColor = UIColor.white // doesn't work
}
}
viewDidLoad is too early
Put the code in viewDidAppear, viewDidLoad is too early. Then your placeholder should change to white
searchController.searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Enter Search Here", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
In viewDidAppear
if iOS is higher than 13.0, use searchBar.searchTextField.attributedPlaceholder.
if iOS is lower than 13.0, use searchBar.value(forKey: "searchField") to access searchTextField
var searchBar = UISearchBar()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if #available(iOS 13.0, *) {
searchBar.searchTextField.attributedPlaceholder = NSAttributedString(string: "Enter Search Here", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
} else {
if let searchField = searchBar.value(forKey: "searchField") as? UITextField {
searchField.attributedPlaceholder = NSAttributedString(string: "Enter Search Here", attributes: [NSAttributedString.Key.foregroundColor: UIColor.white2])
}
}
}
Try this:
var searchBar: UISearchBar!
if let textfield = searchBar.value(forKey: "searchField") as? UITextField {
textfield.attributedPlaceholder = NSAttributedString(string: textfield.placeholder ?? "", attributes: [NSAttributedString.Key.foregroundColor : UIColor.white])
}
Please add this extension and try this !
extension UISearchBar{
var placeholderLabel: UILabel? { return value(forKey: "placeholderLabel") as? UILabel }
func setPlaceholder(textColor: UIColor) {
guard let placeholderLabel = placeholderLabel else { return }
let label = Label(label: placeholderLabel, textColor: textColor)
placeholderLabel.removeFromSuperview() // To remove existing label. Otherwise it will overwrite it if called multiple times.
setValue(label, forKey: "placeholderLabel")
}
}
private extension UITextField {
private class Label: UILabel {
private var _textColor = UIColor.lightGray
override var textColor: UIColor! {
set { super.textColor = _textColor }
get { return _textColor }
}
init(label: UILabel, textColor: UIColor = .lightGray) {
_textColor = textColor
super.init(frame: label.frame)
self.text = label.text
self.font = label.font
}
required init?(coder: NSCoder) { super.init(coder: coder) }
}
Usage
yourSearchBarObject.setPlaceholder(textColor: .red)
You need to style everything in viewDidAppear

Replace font for every UILabel in the App

I'm trying to change the font by make extension for UILabel
extension UILabel{
var defaultFont: UIFont? {
get { return self.font }
set {
let oldFontSize = self.font.pointSize
let oldFontName = self.font.fontName
let newFontName = newValue?.fontName
self.font = UIFont(name: newFontName!, size: oldFontSize)
}
}
}
then call
UILabel.appearance().defaultFont = UIFont.init(name: "My Font", size: 5)
But always self.font is nil
using xcode 10 swift 4.2
Edit:
Now, after changing swift version to Swift3 it works good, then the problem is in Swift4.
Are there a solution or Alternative way to do that ?
try this :
is appDelegate -> didFinishLaunch:
UILabel.appearance().font = UIFont("your font name", size: 15)
Standard way of doing is to create your custom UILabel class and use this class everywhere you want that default font. e.g,
class MyFontLabel: UILabel {
override init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.commonInit()
}
private func commonInit() {
self.font = UIFont(name: "My Font", size: self.font.pointSize)
}
}
So while you technically can't override an instance variable, aka overriding UILabel's font variable won't work, you can extend UILabel.
public class CustomLabel: UILabel {
override public var font: UIFont! {
get { return super.font }
set {
super.font = UIFont(name: "Font-Name", size: super.font.pointSize)
}
}
}
I've found it most helpful to have add a style(as style: TypographyStyle) function in an extension to UILabel, where TypographyStyle is an enum I've defined that contains a bunch of style properties, including font. I also created a convenience init for UILabel that takes a case of TypographyStyle, and calls style(as:), so that the style will be automatically applied when the label is created.
extension UILabel {
convenience init(style: TypographyStyle) {
self.init()
self.style(as: style)
}
func style(as style: TypographyStyle) {
self.font = UIFont(name: style.fontName, size: style.fontSize)
}
}
enum TypographyStyle {
case body, h1, h2, h3
var font: UIFont {
switch self {
// here you can return different fonts for the different styles if you like
default:
return UIFont(name: "AvenirNext", size: 16)
}
}
}
// Usage
let label = UILabel(style: .body)
Put this on appDelegate in didFinishLaunchingWithOptions method:
UILabel.appearance().font = UIFont(name: "your font name", size: UILabel.appearance().font.pointSize)

Custom font MKAnnotationView resize animation

I have achieved to set a custom font and color for my callout using this solution, but it produces a strange animation because it first sets the size according to the previous font and then resizes the box with the new one check this:
Code to change font and color
#objc class CustomAnnotationView: MKAnnotationView {
override func didAddSubview(_ subview: UIView) {
if isSelected {
setNeedsLayout()
}
}
override func layoutSubviews() {
if !isSelected {
return
}
loopViewHierarchy { (view: UIView) -> Bool in
if let label = view as? UILabel {
label.font = ViewUtil.fontMediumWithSize(14)
label.textColor = ViewUtil.BlueGray
return false
}
return true
}
super.layoutSubviews()
}
}
typealias ViewBlock = (_ view: UIView) -> Bool
extension UIView {
func loopViewHierarchy(block: ViewBlock?) {
if block?(self) ?? true {
for subview in subviews {
subview.loopViewHierarchy(block: block)
}
}
}
}

Why Is My IBDesignable UIButton Subclass Not Rendering In Interface Builder?

In the screen shot below please note that the DropDownButton (the selected view) is not being live rendered. Also, please note the "Designables Up To Date" in the Identity Inspector. Finally, please note the two break points in the assistant editor: if I execute Editor -> Debug Selected Views then both of these break points are hit.
Here's what It looks like when I run it:
Here's the code:
#IBDesignable
class DropDownButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
private func initialize() {
if image(for: .normal) == nil {
//setImage(#imageLiteral(resourceName: "DropDown"), for: .normal)
let bundle = Bundle(for: DropDownButton.self)
if let image = UIImage(named: "DropDown", in: bundle, compatibleWith: nil) {
setImage(image, for: .normal) // Editor -> Debug Selected Views reaches this statement
}
}
if title(for: .normal) == nil {
setTitle("DropDown", for: .normal) // Editor -> Debug Selected Views reaches this statement
}
addTarget(self, action: #selector(toggle), for: .touchUpInside)
}
override func layoutSubviews() {
super.layoutSubviews()
if var imageFrame = imageView?.frame, var labelFrame = titleLabel?.frame {
labelFrame.origin.x = contentEdgeInsets.left
imageFrame.origin.x = labelFrame.origin.x + labelFrame.width + 2
imageView?.frame = imageFrame
titleLabel?.frame = labelFrame
}
}
override func setTitle(_ title: String?, for state: UIControlState) {
super.setTitle(title, for: state)
sizeToFit()
}
public var collapsed: Bool {
return imageView?.transform == CGAffineTransform.identity
}
public var expanded: Bool {
return !collapsed
}
private func collapse() {
imageView?.transform = CGAffineTransform.identity
}
private func expand() {
imageView?.transform = CGAffineTransform(rotationAngle: .pi)
}
#objc private func toggle(_: UIButton) {
if collapsed { expand() } else { collapse() }
}
}
First Edit:
I added the prepareForInterfaceBuilder method as per #DonMag's answer. Doing so made an improvement but there is still something wrong: Interface builder seems confused about the frame. When I select the button only the title is selected, not the image (i.e. triangle). I added a border; it goes around both the title and the image. Here is a picture:
If I drag the button to a new position then everything moves, title and image.
Also, it surprised me that prepareForInterfaceBuilder made a difference. My understanding of this method it that it allows me to do interface builder only setup such as providing dummy data.
Add this:
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
initialize()
}

Custom UITableViewCell labels

I'm trying to create a custom UITableViewCell with 3 labels, in a similar style to the subtitle style, but with a label on the right, too. I've got 2 issues, so far:
When creating the labels on the left, I was using a constant 16 offset, but have since found this is incorrect. The separator inset returns 15 (self.separatorInset.left), but iPad the labels seem to be 22.5 from the left. I'll have to do this in code, but is checking for the iPad UI idiom and multiplying by 1.5 the correct way of doing this?
The second issue is that I am using dynamic font sizes: Body for the main label and Caption 1 for the subtitle. However, this creates labels with font size 17 and 12 respectively, while the system cell's labels are 16 and 11. I've got a custom UILabel that tries to ensure system font size changes are respected. Should I make the font size 1 smaller in this class?
class DynamicFontLabel: UILabel {
var fontStyle: String?
convenience override init() {
self.init(fontStyle: UIFontTextStyleBody)
}
init(fontStyle: String) {
super.init()
self.font = UIFont.preferredFontForTextStyle(fontStyle)
self.setup()
}
override init(frame: CGRect) {
super.init(frame: frame)
self.setup()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setup()
}
func setup() {
// Ensure the text color is correct, as per the UILabel appearance
self.textColor = UILabel.appearance().textColor
self.fontStyle = self.currentFontStyle()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateFontSize", name: UIContentSizeCategoryDidChangeNotification, object: nil)
self.numberOfLines = 0
self.lineBreakMode = NSLineBreakMode.ByWordWrapping
}
func currentFontStyle() -> String {
if self.font.isEqual(UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)) {
return UIFontTextStyleHeadline
} else if self.font.isEqual(UIFont.preferredFontForTextStyle(UIFontTextStyleBody)) {
return UIFontTextStyleBody
} else if self.font.isEqual(UIFont.preferredFontForTextStyle(UIFontTextStyleCaption1)) {
return UIFontTextStyleCaption1
} else if self.font.isEqual(UIFont.preferredFontForTextStyle(UIFontTextStyleCaption2)) {
return UIFontTextStyleCaption2
} else if self.font.isEqual(UIFont.preferredFontForTextStyle(UIFontTextStyleFootnote)) {
return UIFontTextStyleFootnote
} else if self.font.isEqual(UIFont.preferredFontForTextStyle(UIFontTextStyleSubheadline)) {
return UIFontTextStyleSubheadline
}
return UIFontTextStyleBody
}
func updateFontSize() {
if let fontStyle = self.fontStyle {
self.font = UIFont.preferredFontForTextStyle(fontStyle)
}
}
deinit {
NSNotificationCenter.defaultCenter().removeObserver(self)
}
}

Resources