GrowingNotificationBanner.swift 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2017-2018 Dalton Hinterscher
  4. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
  5. to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
  6. 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:
  7. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  8. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  9. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
  10. ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
  11. THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  12. */
  13. import UIKit
  14. import SnapKit
  15. open class GrowingNotificationBanner: BaseNotificationBanner {
  16. public enum IconPosition {
  17. case top
  18. case center
  19. }
  20. /// The height of the banner when it is presented
  21. override public var bannerHeight: CGFloat {
  22. get {
  23. if let customBannerHeight = customBannerHeight {
  24. return customBannerHeight
  25. } else {
  26. // Calculate the height based on contents of labels
  27. // Determine available width for displaying the label
  28. var boundingWidth = UIScreen.main.bounds.width - padding * 2
  29. // Substract safeAreaInsets from width, if available
  30. // We have to use keyWindow to ask for safeAreaInsets as `self` only knows its' safeAreaInsets in layoutSubviews
  31. if #available(iOS 11.0, *), let keyWindow = UIApplication.shared.keyWindow {
  32. let safeAreaOffset = keyWindow.safeAreaInsets.left + keyWindow.safeAreaInsets.right
  33. boundingWidth -= safeAreaOffset
  34. }
  35. if leftView != nil {
  36. boundingWidth -= sideViewSize + padding
  37. }
  38. if rightView != nil {
  39. boundingWidth -= sideViewSize + padding
  40. }
  41. let titleHeight = ceil(titleLabel?.sizeThatFits(
  42. CGSize(width: boundingWidth,
  43. height: .greatestFiniteMagnitude)).height ?? 0.0)
  44. let subtitleHeight = ceil(subtitleLabel?.sizeThatFits(
  45. CGSize(width: boundingWidth,
  46. height: .greatestFiniteMagnitude)).height ?? 0.0)
  47. let topOffset: CGFloat = shouldAdjustForNotchFeaturedIphone() ? 44.0 : verticalSpacing
  48. let minHeight: CGFloat = shouldAdjustForNotchFeaturedIphone() ? 88.0 : 64.0
  49. var actualBannerHeight = topOffset + titleHeight + subtitleHeight + verticalSpacing
  50. if !subtitleHeight.isZero && !titleHeight.isZero {
  51. actualBannerHeight += innerSpacing
  52. }
  53. return heightAdjustment + max(actualBannerHeight, minHeight)
  54. }
  55. } set {
  56. customBannerHeight = newValue
  57. }
  58. }
  59. /// Spacing between the last label and the bottom edge of the banner
  60. private let verticalSpacing: CGFloat = 14.0
  61. /// Spacing between title and subtitle
  62. private let innerSpacing: CGFloat = 2.5
  63. /// The bottom most label of the notification if a subtitle is provided
  64. public private(set) var subtitleLabel: TTTAttributedLabel?
  65. /// The view that is presented on the left side of the notification
  66. private var leftView: UIView?
  67. /// The view that is presented on the right side of the notification
  68. private var rightView: UIView?
  69. /// Square size for left/right view if set
  70. private let sideViewSize: CGFloat
  71. /// Font used for the title label
  72. internal var titleFont: UIFont = UIFont.systemFont(ofSize: 17.5, weight: UIFont.Weight.bold)
  73. /// Font used for the subtitle label
  74. internal var subtitleFont: UIFont = UIFont.systemFont(ofSize: 15.0)
  75. public init(title: String? = nil,
  76. subtitle: String? = nil,
  77. leftView: UIView? = nil,
  78. rightView: UIView? = nil,
  79. style: BannerStyle = .info,
  80. colors: BannerColorsProtocol? = nil,
  81. iconPosition: IconPosition = .center,
  82. sideViewSize: CGFloat = 24.0) {
  83. self.leftView = leftView
  84. self.rightView = rightView
  85. self.sideViewSize = sideViewSize
  86. super.init(style: style, colors: colors)
  87. let labelsView = UIStackView()
  88. labelsView.axis = .vertical
  89. labelsView.spacing = innerSpacing
  90. let outerStackView = UIStackView()
  91. outerStackView.spacing = padding
  92. switch iconPosition {
  93. case .top:
  94. outerStackView.alignment = .top
  95. case .center:
  96. outerStackView.alignment = .center
  97. }
  98. if let leftView = leftView {
  99. outerStackView.addArrangedSubview(leftView)
  100. leftView.snp.makeConstraints { $0.size.equalTo(sideViewSize) }
  101. }
  102. outerStackView.addArrangedSubview(labelsView)
  103. if let title = title {
  104. titleLabel = UILabel()
  105. titleLabel!.font = titleFont
  106. titleLabel!.numberOfLines = 0
  107. titleLabel!.textColor = .white
  108. titleLabel!.text = title
  109. titleLabel!.setContentHuggingPriority(.required, for: .vertical)
  110. labelsView.addArrangedSubview(titleLabel!)
  111. }
  112. if let subtitle = subtitle {
  113. subtitleLabel = TTTAttributedLabel.init(frame: .zero)
  114. subtitleLabel!.font = subtitleFont
  115. subtitleLabel!.numberOfLines = 0
  116. subtitleLabel!.textColor = .white
  117. subtitleLabel!.text = subtitle
  118. if title == nil {
  119. subtitleLabel!.setContentHuggingPriority(.required, for: .vertical)
  120. }
  121. labelsView.addArrangedSubview(subtitleLabel!)
  122. }
  123. if let rightView = rightView {
  124. outerStackView.addArrangedSubview(rightView)
  125. rightView.snp.makeConstraints { $0.size.equalTo(sideViewSize) }
  126. }
  127. contentView.addSubview(outerStackView)
  128. outerStackView.snp.makeConstraints { (make) in
  129. if #available(iOS 11.0, *) {
  130. make.left.equalTo(safeAreaLayoutGuide).offset(padding)
  131. make.right.equalTo(safeAreaLayoutGuide).offset(-padding)
  132. } else {
  133. make.left.equalToSuperview().offset(padding)
  134. make.right.equalToSuperview().offset(-padding)
  135. }
  136. make.centerY.equalToSuperview()
  137. }
  138. }
  139. required public init?(coder aDecoder: NSCoder) {
  140. fatalError("init(coder:) has not been implemented")
  141. }
  142. override func spacerViewHeight() -> CGFloat {
  143. return super.spacerViewHeight() + heightAdjustment
  144. }
  145. }
  146. public extension GrowingNotificationBanner {
  147. func applyStyling(cornerRadius: CGFloat? = nil,
  148. titleFont: UIFont? = nil,
  149. titleColor: UIColor? = nil,
  150. titleTextAlign: NSTextAlignment? = nil,
  151. subtitleFont: UIFont? = nil,
  152. subtitleColor: UIColor? = nil,
  153. subtitleTextAlign: NSTextAlignment? = nil) {
  154. if let cornerRadius = cornerRadius {
  155. contentView.layer.cornerRadius = cornerRadius
  156. }
  157. if let titleFont = titleFont {
  158. self.titleFont = titleFont
  159. titleLabel!.font = titleFont
  160. }
  161. if let titleColor = titleColor {
  162. titleLabel!.textColor = titleColor
  163. }
  164. if let titleTextAlign = titleTextAlign {
  165. titleLabel!.textAlignment = titleTextAlign
  166. }
  167. if let subtitleFont = subtitleFont {
  168. self.subtitleFont = subtitleFont
  169. subtitleLabel!.font = subtitleFont
  170. }
  171. if let subtitleColor = subtitleColor {
  172. subtitleLabel!.textColor = subtitleColor
  173. }
  174. if let subtitleTextAlign = subtitleTextAlign {
  175. subtitleLabel!.textAlignment = subtitleTextAlign
  176. }
  177. if titleFont != nil || subtitleFont != nil {
  178. updateBannerHeight()
  179. }
  180. }
  181. }