Skip to content

icon + text = text becomes icon if text match icon name #258

Open

Description

@Slodin

Hi,

In my toolbar item, I have set everything according to the doc page, it was working fine because I was making my app in a different language than English. However, now my app requires English as an alternative language option and this problem occurred.

`let barItemFontAttr = [
NSAttributedString.Key.font: UIFont.fontAwesome(ofSize: 15, style: .solid)
]

refreshOrders.setTitleTextAttributes(barItemFontAttr, for: .normal)
refreshOrders.setTitleTextAttributes(barItemFontAttr, for: .selected)
refreshOrders.title = "(String.fontAwesomeIcon(name: .sync)) Sync" //Don't work
refreshOrders.title = "(String.fontAwesomeIcon(name: .sync)) 同步" //Works`

Expected:
Sync Icon + "Sync"

Got:
Sync Icon + Sync Icon

How am I suppose to do this?

Activity

  1. MatrixSenpai commented on Jun 24, 2020

    @MatrixSenpai
    Collaborator

    This is another problem I'm attempting to solve. When typing the name of an icon in English, the font auto-translates it to the appropriate icon, rather than just using Unicode. I have not yet been able to figure out why.

    Are you initializing your UIBarButtonItem in Storyboard or inside a class?

  2. Slodin commented on Jun 24, 2020

    @Slodin
    Author

    This is another problem I'm attempting to solve. When typing the name of an icon in English, the font auto-translates it to the appropriate icon, rather than just using Unicode. I have not yet been able to figure out why.

    Are you initializing your UIBarButtonItem in Storyboard or inside a class?

    It is a UIBarButtonItem in the Storyboard, but used in a viewcontroller class.
    I thought String.fontAwesomeIcon(name) is used to translate the icon into a string, but it seems to affect the entire title anyways with or without calling that function. It's almost as if it's matching anything that contains certain key words. I was trying to write "Routes Prev", it became: route Icon + "s Prev".

  3. MatrixSenpai commented on Jun 25, 2020

    @MatrixSenpai
    Collaborator

    You're correct, and this is a source of investigation at the moment. I have yet to determine what it is about the font that causes actual text to be translated into an FA icon.
    Currently, when using String.fontAwesomeIcon(name:), it's basically just the same as using FontAwesome.x_icon_name.unicode
    Until I can solve this issue, my recommendations are:

    • Use NSAttributedString where available. UILabel and UIButton both have attributed title options.
    • Use only an icon otherwise

    Here's a short code snippet to get you started with FA+NSAttributedString

    let button = UIButton() // or link to one defined in your Storyboard
    let label = UILabel()
    
    // Note this MUST be Mutable for us to be able to apply an attribute
    let string = NSMutableAttributedString(string: "Sync " + .fontAwesomeIcon(name: .chevronRight))
    
    // If your icon is the first char in the text, use range: NSRange(location: 0, length: 1)
    string.addAttribute(.font, value: UIFont.fontAwesome(ofSize: 18, style: .regular), 
                               range: NSRange(location: string.length - 1, length: 1))
    
    button.setAttributedTitle(string, for: .normal)
    label.attributedText = string

    Hope this helps!

  4. Slodin commented on Jun 26, 2020

    @Slodin
    Author

    Thanks for your reply, I did figure out a similar thing to yours for labels and buttons. However, UIBarButtonItem is a different story since it doesn't take attributed strings. Then I realized I can just stuff a button or label into the UIBarButtonItem...

    So I figured a workaround for UIBarButtonItem, I didn't look into this problem until earlier since I had to do many more translations and this problem only happens at a few places for my project...I hate IOS localization lol.
    It's a super roundabout way...But I don't have any better ideas rn

    View Controller

    let routePreviewButton = UIBarButtonItem()! //linked from storyboard
    let routeButton = UIButton()
    //Add your button target, users will touch this instead of the UIBarButtonItem now in my testing
    routeButton.addTarget(self, action: #selector(navToRoutes), for: .touchUpInside)
    let routeIconTextAttr = generalHelper.getFAIconWithText(name: .map, size: 15, style: .solid, text: "Routes Prev.")
    routeButton.setAttributedTitle(routeIconTextAttr, for: .normal)
    routeButton.setAttributedTitle(routeIconTextAttr, for: .selected)
    routeButton.sizeToFit()
    routePreviewButton.customView = routeButton

    GeneralHelper.getFAIconWithText

    let icon = NSMutableAttributedString(string: String.fontAwesomeIcon(name: name), attributes: [.font: UIFont.fontAwesome(ofSize: CGFloat(size), style: style)])
    let textToAppend = NSAttributedString(string: text, attributes: [.font: UIFont.systemFont(ofSize: CGFloat(size))])
    icon.append(textToAppend)
    return icon

    This approach still requires coders to adjust the button's appearance, but it's a basic start for me to at least get the bar buttons working.

  5. Slodin commented on Jul 11, 2020

    @Slodin
    Author

    So If anyone needs this. Here is a BarButtonItem that works with this library in its current state.
    I'm still new to swift as I just came from Android development since our company is short-handed in the swift side. So there might be better ways to do this, but this works for me.

    import UIKit
    import FontAwesome_swift
    
    @IBDesignable class FAIconBarButtonItem: UIBarButtonItem {
        //MARK: - Variable Declearation
        private let button = UIButton(type: .system)
        
        @IBInspectable private var size: CGFloat = 15 {
            didSet {
                button.titleLabel?.font = UIFont.systemFont(ofSize: size)
                setButtonIcon(icon: icon)
            }
        }
        
        @IBInspectable private var text: String? = "Default" {
            didSet {setButtonTitle(title: text)}
        }
        
        @IBInspectable private var normalColor: UIColor = .systemBlue {
            didSet {
                setButtonTitleColor(normalColor: normalColor)
                setButtonIcon(icon: icon)
            }
        }
        
        private var icon: FontAwesome = .powerOff {
            didSet {setButtonIcon(icon: icon)}
        }
        
        @IBInspectable private var iconAdapter: String {
            get {
                return self.icon.rawValue
            }
            set(iconString) {
                self.icon = FontAwesome(rawValue: iconString) ?? .powerOff
            }
        }
    
        //Used to provide addTarget action
        var touchUpInside: ((_ button: UIButton)->())?
        
        //MARK: - Init
        private override init(){
            super.init()
            target = self
            customView = setup()
        }
        
        required init?(coder aDecorder: NSCoder) {
            super.init(coder: aDecorder)
            customView = setup()
        }
        
        //MARK: - Private Functions
        private func setup() -> UIButton {
            button.isEnabled = true
            button.isUserInteractionEnabled = true
            button.titleLabel?.font = UIFont.systemFont(ofSize: size)
            setButtonTitleColor(normalColor: normalColor)
            setButtonIcon(icon: icon)
            setButtonTitle(title: text)
            button.tintColor = normalColor
            button.addTarget(self, action: #selector(touchUpInside(_:)), for: .touchUpInside)
            button.sizeToFit()
            return button
        }
        
        private func setButtonTitle(title: String?){
            button.setTitle(title?.localized(), for: .normal)
        }
        
        private func setButtonTitleColor(normalColor: UIColor){
            button.setTitleColor(normalColor, for: .normal)
        }
        
        private func setButtonIcon(icon: FontAwesome){
            //All changes of icon, color, and size is made in this
            let normalIconImage = UIImage.fontAwesomeIcon(name: icon, style: .solid, textColor: normalColor, size: CGSize(width: size, height: size))
            button.tintColor = normalColor
            button.setImage(normalIconImage, for: .normal)
        }
        
        @objc private func touchUpInside(_ sender: UIButton){
            touchUpInside?(sender)
        }
        
        //MARK: - Exposed Functions
        func setTitleAndIcon(title: String, icon: FontAwesome) {
            setIcon(icon: icon)
            setTitle(title: title)
        }
        
        func setTitle(title: String){
            self.text = title
        }
        
        func setIcon(icon: FontAwesome){
            self.icon = icon
        }
        
        func setColor(color: UIColor){
            self.normalColor = color
        }
    }

    Usage:

    1. Make your barButtonItem in your storyboard
    2. Use this FAIconBarButtonItem class
    3. You can customize a few things in the inspector

    How to get button's action when pressed after linking your FABarButtonItem outlet
    In your ViewConroller

    barButtonItem.touchUpInside = {
       //whatever you want to do
       //If you need other interaction methods, modify FABarButtonItem and use "touchUpInside" as an example.
    }
    //Change title
    setTitle(title: String)
    
    //Change icon
    setIcon(icon: FontAwesome)
    
    //Change title and icon
    setTitleAndIcon(title: String, icon: FontAwesome)
    
    //Change color
    setColor(color: UIColor)

    It should also work with code init but it's not in my use case so I didn't care, you can modify my code to get it working if it's not already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions