Instantly share code, notes, and snippets.
nebiros/LayoutableButton.swift
Created Feb 28, 2017
import UIKit | |
enum LayoutableButtonVerticalAlignment: String { | |
case center | |
case top | |
case bottom | |
case unset | |
} | |
enum LayoutableButtonHorizontalAlignment: String { | |
case center | |
case left | |
case right | |
case unset | |
} | |
@IBDesignable | |
class LayoutableButton: UIButton { | |
@IBInspectable | |
var imageToTitleSpacing: CGFloat = 8.0 { | |
didSet { | |
setNeedsLayout() | |
} | |
} | |
var imageVerticalAlignment: LayoutableButtonVerticalAlignment = .unset { | |
didSet { | |
setNeedsLayout() | |
} | |
} | |
var imageHorizontalAlignment: LayoutableButtonHorizontalAlignment = .unset { | |
didSet { | |
setNeedsLayout() | |
} | |
} | |
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageVerticalAlignment' instead.") | |
@IBInspectable | |
var imageVerticalAlignmentName: String! { | |
willSet { | |
if let newImageVerticalAlignmentName = LayoutableButtonVerticalAlignment(rawValue: newValue.lowercased()) { | |
imageVerticalAlignment = newImageVerticalAlignmentName | |
} | |
} | |
} | |
@available(*, unavailable, message: "This property is reserved for Interface Builder. Use 'imageHorizontalAlignmentName' instead.") | |
@IBInspectable | |
var imageHorizontalAlignmentName: String! { | |
willSet { | |
if let newImageHorizontalAlignmentName = LayoutableButtonHorizontalAlignment(rawValue: newValue.lowercased()) { | |
imageHorizontalAlignment = newImageHorizontalAlignmentName | |
} | |
} | |
} | |
var extraContentEdgeInsets: UIEdgeInsets = UIEdgeInsets.zero | |
override var contentEdgeInsets: UIEdgeInsets { | |
get { | |
return super.contentEdgeInsets | |
} | |
set { | |
super.contentEdgeInsets = newValue | |
self.extraContentEdgeInsets = newValue | |
} | |
} | |
var extraImageEdgeInsets: UIEdgeInsets = UIEdgeInsets.zero | |
override var imageEdgeInsets: UIEdgeInsets { | |
get { | |
return super.imageEdgeInsets | |
} | |
set { | |
super.imageEdgeInsets = newValue | |
self.extraImageEdgeInsets = newValue | |
} | |
} | |
var extraTitleEdgeInsets: UIEdgeInsets = UIEdgeInsets.zero | |
override var titleEdgeInsets: UIEdgeInsets { | |
get { | |
return super.titleEdgeInsets | |
} | |
set { | |
super.titleEdgeInsets = newValue | |
self.extraTitleEdgeInsets = newValue | |
} | |
} | |
required init?(coder aDecoder: NSCoder) { | |
super.init(coder: aDecoder) | |
self.imageEdgeInsets = super.imageEdgeInsets | |
self.titleEdgeInsets = super.titleEdgeInsets | |
self.contentEdgeInsets = super.contentEdgeInsets | |
} | |
override init(frame: CGRect) { | |
super.init(frame: frame) | |
} | |
override func layoutSubviews() { | |
if let imageSize = self.imageView?.image?.size, | |
let font = self.titleLabel?.font, | |
let textSize = self.titleLabel?.attributedText?.size() ?? self.titleLabel?.text?.size(attributes: [NSFontAttributeName: font]) { | |
var imageEdgeInsets = UIEdgeInsets.zero | |
var titleEdgeInsets = UIEdgeInsets.zero | |
var contentEdgeInsets = UIEdgeInsets.zero | |
let halfImageToTitleSpacing = imageToTitleSpacing / 2.0 | |
switch imageVerticalAlignment { | |
case .bottom: | |
imageEdgeInsets.top = (textSize.height + imageToTitleSpacing) / 2.0 | |
imageEdgeInsets.bottom = (-textSize.height - imageToTitleSpacing) / 2.0 | |
titleEdgeInsets.top = (-imageSize.height - imageToTitleSpacing) / 2.0 | |
titleEdgeInsets.bottom = (imageSize.height + imageToTitleSpacing) / 2.0 | |
contentEdgeInsets.top = (min(imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 | |
contentEdgeInsets.bottom = (min(imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 | |
//only works with contentVerticalAlignment = .center | |
contentVerticalAlignment = .center | |
case .top: | |
imageEdgeInsets.top = (-textSize.height - imageToTitleSpacing) / 2.0 | |
imageEdgeInsets.bottom = (textSize.height + imageToTitleSpacing) / 2.0 | |
titleEdgeInsets.top = (imageSize.height + imageToTitleSpacing) / 2.0 | |
titleEdgeInsets.bottom = (-imageSize.height - imageToTitleSpacing) / 2.0 | |
contentEdgeInsets.top = (min(imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 | |
contentEdgeInsets.bottom = (min(imageSize.height, textSize.height) + imageToTitleSpacing) / 2.0 | |
//only works with contentVerticalAlignment = .center | |
contentVerticalAlignment = .center | |
case .center: | |
//only works with contentVerticalAlignment = .center | |
contentVerticalAlignment = .center | |
break | |
case .unset: | |
break | |
} | |
switch imageHorizontalAlignment { | |
case .left: | |
imageEdgeInsets.left = -halfImageToTitleSpacing | |
imageEdgeInsets.right = halfImageToTitleSpacing | |
titleEdgeInsets.left = halfImageToTitleSpacing | |
titleEdgeInsets.right = -halfImageToTitleSpacing | |
contentEdgeInsets.left = halfImageToTitleSpacing | |
contentEdgeInsets.right = halfImageToTitleSpacing | |
case .right: | |
imageEdgeInsets.left = textSize.width + halfImageToTitleSpacing | |
imageEdgeInsets.right = -textSize.width - halfImageToTitleSpacing | |
titleEdgeInsets.left = -imageSize.width - halfImageToTitleSpacing | |
titleEdgeInsets.right = imageSize.width + halfImageToTitleSpacing | |
contentEdgeInsets.left = halfImageToTitleSpacing | |
contentEdgeInsets.right = halfImageToTitleSpacing | |
case .center: | |
imageEdgeInsets.left = textSize.width / 2.0 | |
imageEdgeInsets.right = -textSize.width / 2.0 | |
titleEdgeInsets.left = -imageSize.width / 2.0 | |
titleEdgeInsets.right = imageSize.width / 2.0 | |
contentEdgeInsets.left = -((imageSize.width + textSize.width) - max(imageSize.width, textSize.width)) / 2.0 | |
contentEdgeInsets.right = -((imageSize.width + textSize.width) - max(imageSize.width, textSize.width)) / 2.0 | |
case .unset: | |
break | |
} | |
contentEdgeInsets.top += extraContentEdgeInsets.top | |
contentEdgeInsets.bottom += extraContentEdgeInsets.bottom | |
contentEdgeInsets.left += extraContentEdgeInsets.left | |
contentEdgeInsets.right += extraContentEdgeInsets.right | |
imageEdgeInsets.top += extraImageEdgeInsets.top | |
imageEdgeInsets.bottom += extraImageEdgeInsets.bottom | |
imageEdgeInsets.left += extraImageEdgeInsets.left | |
imageEdgeInsets.right += extraImageEdgeInsets.right | |
titleEdgeInsets.top += extraTitleEdgeInsets.top | |
titleEdgeInsets.bottom += extraTitleEdgeInsets.bottom | |
titleEdgeInsets.left += extraTitleEdgeInsets.left | |
titleEdgeInsets.right += extraTitleEdgeInsets.right | |
super.imageEdgeInsets = imageEdgeInsets | |
super.titleEdgeInsets = titleEdgeInsets | |
super.contentEdgeInsets = contentEdgeInsets | |
} else { | |
super.imageEdgeInsets = extraImageEdgeInsets | |
super.titleEdgeInsets = extraTitleEdgeInsets | |
super.contentEdgeInsets = extraContentEdgeInsets | |
} | |
super.layoutSubviews() | |
} | |
} |
This comment has been minimized.
This comment has been minimized.
Hi @Nebrios, how to make it work for the Arabic language? Because when I use the same code for it works totally different. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
Based on: http://stackoverflow.com/a/41744464/255463