// // ButtonWithImageAndTitleExtension.swift // ButtonWithImageAndTitleDemo // // Created by Mervyn Ong on 24/10/14. // Copyright (c) 2014 mervynokm. All rights reserved. // // // The MIT License (MIT) // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. import UIKit @objc extension UIButton { /// Enum to determine the title position with respect to the button image /// /// - top: title above button image /// - bottom: title below button image /// - left: title to the left of button image /// - right: title to the right of button image @objc enum Position: Int { case top, bottom, left, right } /// This method sets an image and title for a UIButton and /// repositions the titlePosition with respect to the button image. /// /// - Parameters: /// - image: Button image /// - title: Button title /// - titlePosition: UIViewContentModeTop, UIViewContentModeBottom, UIViewContentModeLeft or UIViewContentModeRight /// - additionalSpacing: Spacing between image and title /// - state: State to apply this behaviour @objc func set(image: UIImage?, title: String, titlePosition: Position, additionalSpacing: CGFloat, state: UIControl.State){ imageView?.contentMode = .center setImage(image, for: state) setTitle(title, for: state) titleLabel?.contentMode = .center adjust(title: title as NSString, at: titlePosition, with: additionalSpacing) } /// This method sets an image and an attributed title for a UIButton and /// repositions the titlePosition with respect to the button image. /// /// - Parameters: /// - image: Button image /// - title: Button attributed title /// - titlePosition: UIViewContentModeTop, UIViewContentModeBottom, UIViewContentModeLeft or UIViewContentModeRight /// - additionalSpacing: Spacing between image and title /// - state: State to apply this behaviour @objc func set(image: UIImage?, attributedTitle title: NSAttributedString, at position: Position, width spacing: CGFloat, state: UIControl.State){ imageView?.contentMode = .center setImage(image, for: state) adjust(attributedTitle: title, at: position, with: spacing) titleLabel?.contentMode = .center setAttributedTitle(title, for: state) } // MARK: Private Methods @objc private func adjust(title: NSString, at position: Position, with spacing: CGFloat) { let imageRect: CGRect = self.imageRect(forContentRect: frame) // Use predefined font, otherwise use the default let titleFont: UIFont = titleLabel?.font ?? UIFont() let titleSize: CGSize = title.size(withAttributes: [NSAttributedString.Key.font: titleFont]) arrange(titleSize: titleSize, imageRect: imageRect, atPosition: position, withSpacing: spacing) } @objc private func adjust(attributedTitle: NSAttributedString, at position: Position, with spacing: CGFloat) { let imageRect: CGRect = self.imageRect(forContentRect: frame) let titleSize = attributedTitle.size() arrange(titleSize: titleSize, imageRect: imageRect, atPosition: position, withSpacing: spacing) } @objc private func arrange(titleSize: CGSize, imageRect:CGRect, atPosition position: Position, withSpacing spacing: CGFloat) { switch (position) { case .top: titleEdgeInsets = UIEdgeInsets(top: -(imageRect.height + titleSize.height + spacing), left: -(imageRect.width), bottom: 0, right: 0) imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -titleSize.width) contentEdgeInsets = UIEdgeInsets(top: spacing / 2 + titleSize.height, left: -imageRect.width/2, bottom: 0, right: -imageRect.width/2) case .bottom: titleEdgeInsets = UIEdgeInsets(top: (imageRect.height + titleSize.height + spacing), left: -(imageRect.width), bottom: 0, right: 0) imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -titleSize.width) contentEdgeInsets = UIEdgeInsets(top: 0, left: -imageRect.width/2, bottom: spacing / 2 + titleSize.height, right: -imageRect.width/2) case .left: titleEdgeInsets = UIEdgeInsets(top: 0, left: -(imageRect.width * 2), bottom: 0, right: 0) imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -(titleSize.width * 2 + spacing)) contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing / 2) case .right: titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: -spacing) imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: spacing / 2) } } }