// // Copyright 2017 Signal Messenger, LLC // SPDX-License-Identifier: AGPL-3.0-only // import SignalServiceKit import UIKit public class OWSFlatButton: UIView { public let button: UIButton private var pressedBlock: (() -> Void)? private var upColor: UIColor? private var downColor: UIColor? public var cornerRadius: CGFloat { get { button.layer.cornerRadius } set { button.layer.cornerRadius = newValue button.clipsToBounds = newValue > 0 } } public override var accessibilityIdentifier: String? { didSet { guard let accessibilityIdentifier = self.accessibilityIdentifier else { return } button.accessibilityIdentifier = "\(accessibilityIdentifier).button" } } override public var backgroundColor: UIColor? { willSet { owsFailDebug("Use setBackgroundColors(upColor:) instead.") } } public var titleEdgeInsets: UIEdgeInsets { get { return button.titleEdgeInsets } set { button.titleEdgeInsets = newValue } } public var contentEdgeInsets: UIEdgeInsets { get { return button.contentEdgeInsets } set { button.contentEdgeInsets = newValue } } public override var tintColor: UIColor! { get { return button.tintColor } set { button.tintColor = newValue } } public init() { AssertIsOnMainThread() button = UIButton(type: .custom) super.init(frame: CGRect.zero) createContent() } @available(*, unavailable, message: "use other constructor instead.") required public init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func createContent() { self.addSubview(button) button.addTarget(self, action: #selector(buttonPressed), for: .touchUpInside) button.autoPinEdgesToSuperviewEdges() } public class func button(title: String, font: UIFont, titleColor: UIColor, backgroundColor: UIColor, width: CGFloat, height: CGFloat, target: Any, selector: Selector) -> OWSFlatButton { let button = OWSFlatButton() button.setTitle(title: title, font: font, titleColor: titleColor ) button.setBackgroundColors(upColor: backgroundColor) button.useDefaultCornerRadius() button.setSize(width: width, height: height) button.addTarget(target: target, selector: selector) return button } public class func button(title: String, titleColor: UIColor, backgroundColor: UIColor, width: CGFloat, height: CGFloat, target: Any, selector: Selector) -> OWSFlatButton { return OWSFlatButton.button(title: title, font: fontForHeight(height), titleColor: titleColor, backgroundColor: backgroundColor, width: width, height: height, target: target, selector: selector) } public class func button(title: String, font: UIFont, titleColor: UIColor, backgroundColor: UIColor, target: Any, selector: Selector) -> OWSFlatButton { return OWSFlatButton.button( title: title, font: font, titleColor: titleColor, backgroundColor: backgroundColor, target: target, selector: selector, cornerRadius: .defaultCornerStyle ) } public class func insetButton( title: String, font: UIFont, titleColor: UIColor, backgroundColor: UIColor, target: Any, selector: Selector ) -> OWSFlatButton { return OWSFlatButton.button( title: title, font: font, titleColor: titleColor, backgroundColor: backgroundColor, target: target, selector: selector, cornerRadius: .insetCornerStyle ) } public class func button( title: String, font: UIFont, titleColor: UIColor, backgroundColor: UIColor, target: Any, selector: Selector, cornerRadius: CGFloat ) -> OWSFlatButton { let button = OWSFlatButton() button.setTitle(title: title, font: font, titleColor: titleColor ) button.setBackgroundColors(upColor: backgroundColor) button.addTarget(target: target, selector: selector) button.layer.cornerRadius = cornerRadius button.clipsToBounds = true return button } public class func fontForHeight(_ height: CGFloat) -> UIFont { // Cap the "button height" at 40pt or button text can look // excessively large. let fontPointSize = round(min(40, height) * 0.45) return UIFont.semiboldFont(ofSize: fontPointSize) } public class func heightForFont(_ font: UIFont) -> CGFloat { font.lineHeight * 2.5 } // MARK: Methods public func setTitleColor(_ color: UIColor) { button.setTitleColor(color, for: .normal) } public func setTitle(title: String? = nil, font: UIFont? = nil, titleColor: UIColor? = nil) { title.map { button.setTitle($0, for: .normal) } font.map { button.titleLabel?.font = $0 } titleColor.map { setTitleColor($0) } } public func setAttributedTitle(_ title: NSAttributedString) { button.setAttributedTitle(title, for: .normal) } public func setImage(_ image: UIImage) { button.setImage(image, for: .normal) } public func setBackgroundColors(upColor: UIColor, downColor: UIColor ) { button.setBackgroundImage(UIImage.image(color: upColor), for: .normal) button.setBackgroundImage(UIImage.image(color: downColor), for: .highlighted) } public func setBackgroundColors(upColor: UIColor) { let downColor = upColor == .clear ? upColor : upColor.withAlphaComponent(0.7) setBackgroundColors(upColor: upColor, downColor: downColor) } public func setSize(width: CGFloat, height: CGFloat) { button.autoSetDimension(.width, toSize: width) button.autoSetDimension(.height, toSize: height) } public func useDefaultCornerRadius() { // To my eye, this radius tends to look right regardless of button size // (within reason) or device size. button.layer.cornerRadius = 5 button.clipsToBounds = true } public func useInsetCornerRadius() { button.layer.cornerRadius = 14 button.clipsToBounds = true } public func setEnabled(_ isEnabled: Bool) { button.isEnabled = isEnabled } public func addTarget(target: Any, selector: Selector) { button.addTarget(target, action: selector, for: .touchUpInside) } public func setPressedBlock(_ pressedBlock: @escaping () -> Void) { guard self.pressedBlock == nil else { return } self.pressedBlock = pressedBlock } @objc private func buttonPressed() { pressedBlock?() } public func enableMultilineLabel() { button.titleLabel?.numberOfLines = 0 button.titleLabel?.lineBreakMode = .byWordWrapping button.titleLabel?.textAlignment = .center } public var font: UIFont? { return button.titleLabel?.font } public func autoSetHeightUsingFont(extraVerticalInsets: CGFloat = 0) { guard let font = font else { owsFailDebug("Missing button font.") return } autoSetDimension(.height, toSize: Self.heightForFont(font) + CGFloat(extraVerticalInsets * 2.0)) } override public var intrinsicContentSize: CGSize { button.intrinsicContentSize } } fileprivate extension CGFloat { static var defaultCornerStyle = 5.0 static var insetCornerStyle = 14.0 }