AlertViewController.swift 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. //
  2. // AlertViewController.swift
  3. // OneUP
  4. //
  5. // Created by Daniel Lozano on 5/10/16.
  6. // Copyright © 2016 Icalia Labs. All rights reserved.
  7. //
  8. import UIKit
  9. public typealias AlertActionHandler = ((AlertAction) -> Void)
  10. /// Describes each action that is going to be shown in the 'AlertViewController'
  11. public class AlertAction {
  12. public let title: String
  13. public let style: AlertActionStyle
  14. public let handler: AlertActionHandler?
  15. /**
  16. Initialized an 'AlertAction'
  17. - parameter title: The title for the action, that will be used as the title for a button in the alert controller
  18. - parameter style: The style for the action, that will be used to style a button in the alert controller.
  19. - parameter handler: The handler for the action, that will be called when the user clicks on a button in the alert controller.
  20. - returns: An inmutable AlertAction object
  21. */
  22. public init(title: String, style: AlertActionStyle, handler: AlertActionHandler?) {
  23. self.title = title
  24. self.style = style
  25. self.handler = handler
  26. }
  27. }
  28. /**
  29. Describes the style for an action, that will be used to style a button in the alert controller.
  30. - Default: Green text label. Meant to draw attention to the action.
  31. - Cancel: Gray text label. Meant to be neutral.
  32. - Destructive: Red text label. Meant to warn the user about the action.
  33. */
  34. public enum AlertActionStyle {
  35. case `default`
  36. case cancel
  37. case destructive
  38. case custom(textColor: UIColor)
  39. /**
  40. Decides which color to use for each style
  41. - returns: UIColor representing the color for the current style
  42. */
  43. func color() -> UIColor {
  44. switch self {
  45. case .default:
  46. return ColorPalette.greenColor
  47. case .cancel:
  48. return ColorPalette.grayColor
  49. case .destructive:
  50. return ColorPalette.redColor
  51. case let .custom(color):
  52. return color
  53. }
  54. }
  55. }
  56. private enum Font: String {
  57. case Montserrat = "Montserrat-Regular"
  58. case SourceSansPro = "SourceSansPro-Regular"
  59. func font(_ size: CGFloat = 15.0) -> UIFont {
  60. return UIFont(name: self.rawValue, size: size)!
  61. }
  62. }
  63. /// UIViewController subclass that displays the alert
  64. public class AlertViewController: UIViewController {
  65. /// Text that will be used as the title for the alert
  66. public var titleText: String?
  67. /// Text that will be used as the body for the alert
  68. public var bodyText: String?
  69. /// If set to false, alert wont auto-dismiss the controller when an action is clicked. Dismissal will be up to the action's handler. Default is true.
  70. public var autoDismiss: Bool = true
  71. /// If autoDismiss is set to true, then set this property if you want the dismissal to be animated. Default is true.
  72. public var dismissAnimated: Bool = true
  73. fileprivate var actions = [AlertAction]()
  74. @IBOutlet weak var titleLabel: UILabel!
  75. @IBOutlet weak var bodyLabel: UILabel!
  76. @IBOutlet weak var firstButton: UIButton!
  77. @IBOutlet weak var secondButton: UIButton!
  78. @IBOutlet weak var firstButtonWidthConstraint: NSLayoutConstraint!
  79. override public func loadView() {
  80. let name = "AlertViewController"
  81. let bundle = Bundle(for: type(of: self))
  82. guard let view = bundle.loadNibNamed(name, owner: self, options: nil)?.first as? UIView else {
  83. fatalError("Nib not found.")
  84. }
  85. self.view = view
  86. }
  87. override public func viewDidLoad() {
  88. super.viewDidLoad()
  89. if actions.isEmpty {
  90. let okAction = AlertAction(title: "ok 🕶", style: .default, handler: nil)
  91. addAction(okAction)
  92. }
  93. loadFonts
  94. setupFonts()
  95. setupLabels()
  96. setupButtons()
  97. }
  98. override public func didReceiveMemoryWarning() {
  99. super.didReceiveMemoryWarning()
  100. }
  101. override public func updateViewConstraints() {
  102. if actions.count == 1 {
  103. // If only one action, second button will have been removed from superview
  104. // So, need to add constraint for first button trailing to superview
  105. if let constraint = firstButtonWidthConstraint {
  106. view.removeConstraint(constraint)
  107. }
  108. let views: [String: UIView] = ["button" : firstButton]
  109. let constraints = NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[button]-0-|",
  110. options: NSLayoutConstraint.FormatOptions(rawValue: 0),
  111. metrics: nil,
  112. views: views)
  113. view.addConstraints(constraints)
  114. }
  115. super.updateViewConstraints()
  116. }
  117. // MARK: AlertAction's
  118. /**
  119. Adds an 'AlertAction' to the alert controller. There can be maximum 2 actions. Any more will be ignored. The order is important.
  120. - parameter action: The 'AlertAction' to be added
  121. */
  122. public func addAction(_ action: AlertAction) {
  123. guard actions.count < 2 else { return }
  124. actions += [action]
  125. }
  126. // MARK: Setup
  127. fileprivate func setupFonts() {
  128. titleLabel.font = Font.Montserrat.font()
  129. bodyLabel.font = Font.SourceSansPro.font()
  130. firstButton.titleLabel?.font = Font.Montserrat.font(11.0)
  131. secondButton.titleLabel?.font = Font.Montserrat.font(11.0)
  132. }
  133. fileprivate func setupLabels() {
  134. titleLabel.text = titleText ?? "Alert"
  135. bodyLabel.text = bodyText ?? "This is an alert."
  136. }
  137. fileprivate func setupButtons() {
  138. guard let firstAction = actions.first else { return }
  139. apply(firstAction, toButton: firstButton)
  140. if actions.count == 2 {
  141. let secondAction = actions.last!
  142. apply(secondAction, toButton: secondButton)
  143. } else {
  144. secondButton.removeFromSuperview()
  145. }
  146. }
  147. fileprivate func apply(_ action: AlertAction, toButton: UIButton) {
  148. let title = action.title.uppercased()
  149. let style = action.style
  150. toButton.setTitle(title, for: UIControl.State())
  151. toButton.setTitleColor(style.color(), for: UIControl.State())
  152. }
  153. // MARK: IBAction's
  154. @IBAction func didSelectFirstAction(_ sender: AnyObject) {
  155. guard let firstAction = actions.first else { return }
  156. if let handler = firstAction.handler {
  157. handler(firstAction)
  158. }
  159. dismiss()
  160. }
  161. @IBAction func didSelectSecondAction(_ sender: AnyObject) {
  162. guard let secondAction = actions.last, actions.count == 2 else { return }
  163. if let handler = secondAction.handler {
  164. handler(secondAction)
  165. }
  166. dismiss()
  167. }
  168. // MARK: Helper's
  169. func dismiss() {
  170. guard autoDismiss else { return }
  171. self.dismiss(animated: dismissAnimated, completion: nil)
  172. }
  173. }
  174. // MARK: - Font Loading
  175. let loadFonts: () = {
  176. let loadedFontMontserrat = AlertViewController.loadFont(Font.Montserrat.rawValue)
  177. let loadedFontSourceSansPro = AlertViewController.loadFont(Font.SourceSansPro.rawValue)
  178. if loadedFontMontserrat && loadedFontSourceSansPro {
  179. print("LOADED FONTS")
  180. }
  181. }()
  182. extension AlertViewController {
  183. static func loadFont(_ name: String) -> Bool {
  184. let bundle = Bundle(for: self)
  185. guard let fontPath = bundle.path(forResource: name, ofType: "ttf"),
  186. let data = try? Data(contentsOf: URL(fileURLWithPath: fontPath)),
  187. let provider = CGDataProvider(data: data as CFData),
  188. let font = CGFont(provider)
  189. else {
  190. return false
  191. }
  192. var error: Unmanaged<CFError>?
  193. let success = CTFontManagerRegisterGraphicsFont(font, &error)
  194. if !success {
  195. print("Error loading font. Font is possibly already registered.")
  196. return false
  197. }
  198. return true
  199. }
  200. }