IQToolbar.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. //
  2. // IQToolbar.swift
  3. // https://github.com/hackiftekhar/IQKeyboardManager
  4. // Copyright (c) 2013-16 Iftekhar Qurashi.
  5. //
  6. // Permission is hereby granted, free of charge, to any person obtaining a copy
  7. // of this software and associated documentation files (the "Software"), to deal
  8. // in the Software without restriction, including without limitation the rights
  9. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. // copies of the Software, and to permit persons to whom the Software is
  11. // furnished to do so, subject to the following conditions:
  12. //
  13. // The above copyright notice and this permission notice shall be included in
  14. // all copies or substantial portions of the Software.
  15. //
  16. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. // THE SOFTWARE.
  23. import UIKit
  24. /** @abstract IQToolbar for IQKeyboardManager. */
  25. open class IQToolbar: UIToolbar, UIInputViewAudioFeedback {
  26. private static var _classInitialize: Void = classInitialize()
  27. private class func classInitialize() {
  28. let appearanceProxy = self.appearance()
  29. appearanceProxy.barTintColor = nil
  30. let positions: [UIBarPosition] = [.any, .bottom, .top, .topAttached]
  31. for position in positions {
  32. appearanceProxy.setBackgroundImage(nil, forToolbarPosition: position, barMetrics: .default)
  33. appearanceProxy.setShadowImage(nil, forToolbarPosition: .any)
  34. }
  35. //Background color
  36. appearanceProxy.backgroundColor = nil
  37. }
  38. /**
  39. Previous bar button of toolbar.
  40. */
  41. private var privatePreviousBarButton: IQBarButtonItem?
  42. @objc open var previousBarButton: IQBarButtonItem {
  43. get {
  44. if privatePreviousBarButton == nil {
  45. privatePreviousBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
  46. privatePreviousBarButton?.accessibilityLabel = "Previous"
  47. }
  48. return privatePreviousBarButton!
  49. }
  50. set (newValue) {
  51. privatePreviousBarButton = newValue
  52. }
  53. }
  54. /**
  55. Next bar button of toolbar.
  56. */
  57. private var privateNextBarButton: IQBarButtonItem?
  58. @objc open var nextBarButton: IQBarButtonItem {
  59. get {
  60. if privateNextBarButton == nil {
  61. privateNextBarButton = IQBarButtonItem(image: nil, style: .plain, target: nil, action: nil)
  62. privateNextBarButton?.accessibilityLabel = "Next"
  63. }
  64. return privateNextBarButton!
  65. }
  66. set (newValue) {
  67. privateNextBarButton = newValue
  68. }
  69. }
  70. /**
  71. Title bar button of toolbar.
  72. */
  73. private var privateTitleBarButton: IQTitleBarButtonItem?
  74. @objc open var titleBarButton: IQTitleBarButtonItem {
  75. get {
  76. if privateTitleBarButton == nil {
  77. privateTitleBarButton = IQTitleBarButtonItem(title: nil)
  78. privateTitleBarButton?.accessibilityLabel = "Title"
  79. }
  80. return privateTitleBarButton!
  81. }
  82. set (newValue) {
  83. privateTitleBarButton = newValue
  84. }
  85. }
  86. /**
  87. Done bar button of toolbar.
  88. */
  89. private var privateDoneBarButton: IQBarButtonItem?
  90. @objc open var doneBarButton: IQBarButtonItem {
  91. get {
  92. if privateDoneBarButton == nil {
  93. privateDoneBarButton = IQBarButtonItem(title: nil, style: .done, target: nil, action: nil)
  94. privateDoneBarButton?.accessibilityLabel = "Done"
  95. }
  96. return privateDoneBarButton!
  97. }
  98. set (newValue) {
  99. privateDoneBarButton = newValue
  100. }
  101. }
  102. /**
  103. Fixed space bar button of toolbar.
  104. */
  105. private var privateFixedSpaceBarButton: IQBarButtonItem?
  106. @objc open var fixedSpaceBarButton: IQBarButtonItem {
  107. get {
  108. if privateFixedSpaceBarButton == nil {
  109. privateFixedSpaceBarButton = IQBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
  110. }
  111. privateFixedSpaceBarButton!.isSystemItem = true
  112. if #available(iOS 10, *) {
  113. privateFixedSpaceBarButton!.width = 6
  114. } else {
  115. privateFixedSpaceBarButton!.width = 20
  116. }
  117. return privateFixedSpaceBarButton!
  118. }
  119. set (newValue) {
  120. privateFixedSpaceBarButton = newValue
  121. }
  122. }
  123. override init(frame: CGRect) {
  124. _ = IQToolbar._classInitialize
  125. super.init(frame: frame)
  126. sizeToFit()
  127. autoresizingMask = .flexibleWidth
  128. self.isTranslucent = true
  129. }
  130. @objc required public init?(coder aDecoder: NSCoder) {
  131. _ = IQToolbar._classInitialize
  132. super.init(coder: aDecoder)
  133. sizeToFit()
  134. autoresizingMask = .flexibleWidth
  135. self.isTranslucent = true
  136. }
  137. @objc override open func sizeThatFits(_ size: CGSize) -> CGSize {
  138. var sizeThatFit = super.sizeThatFits(size)
  139. sizeThatFit.height = 44
  140. return sizeThatFit
  141. }
  142. @objc override open var tintColor: UIColor! {
  143. didSet {
  144. if let unwrappedItems = items {
  145. for item in unwrappedItems {
  146. item.tintColor = tintColor
  147. }
  148. }
  149. }
  150. }
  151. @objc override open var barStyle: UIBarStyle {
  152. didSet {
  153. if titleBarButton.selectableTitleColor == nil {
  154. if barStyle == .default {
  155. titleBarButton.titleButton?.setTitleColor(UIColor.init(red: 0.0, green: 0.5, blue: 1.0, alpha: 1), for: .normal)
  156. } else {
  157. titleBarButton.titleButton?.setTitleColor(UIColor.yellow, for: .normal)
  158. }
  159. }
  160. }
  161. }
  162. @objc override open func layoutSubviews() {
  163. super.layoutSubviews()
  164. //If running on Xcode9 (iOS11) only then we'll validate for iOS version, otherwise for older versions of Xcode (iOS10 and below) we'll just execute the tweak
  165. #if swift(>=3.2)
  166. if #available(iOS 11, *) {
  167. return
  168. } else if let customTitleView = titleBarButton.customView {
  169. var leftRect = CGRect.null
  170. var rightRect = CGRect.null
  171. var isTitleBarButtonFound = false
  172. let sortedSubviews = self.subviews.sorted(by: { (view1: UIView, view2: UIView) -> Bool in
  173. if view1.frame.minX != view2.frame.minX {
  174. return view1.frame.minX < view2.frame.minX
  175. } else {
  176. return view1.frame.minY < view2.frame.minY
  177. }
  178. })
  179. for barButtonItemView in sortedSubviews {
  180. if isTitleBarButtonFound == true {
  181. rightRect = barButtonItemView.frame
  182. break
  183. } else if barButtonItemView === customTitleView {
  184. isTitleBarButtonFound = true
  185. //If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem)
  186. } else if barButtonItemView.isKind(of: UIControl.self) == true {
  187. leftRect = barButtonItemView.frame
  188. }
  189. }
  190. let titleMargin: CGFloat = 16
  191. let maxWidth: CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX)
  192. let maxHeight = self.frame.height
  193. let sizeThatFits = customTitleView.sizeThatFits(CGSize(width: maxWidth, height: maxHeight))
  194. var titleRect: CGRect
  195. if sizeThatFits.width > 0 && sizeThatFits.height > 0 {
  196. let width = min(sizeThatFits.width, maxWidth)
  197. let height = min(sizeThatFits.height, maxHeight)
  198. var xPosition: CGFloat
  199. if leftRect.isNull == false {
  200. xPosition = titleMargin + leftRect.maxX + ((maxWidth - width)/2)
  201. } else {
  202. xPosition = titleMargin
  203. }
  204. let yPosition = (maxHeight - height)/2
  205. titleRect = CGRect(x: xPosition, y: yPosition, width: width, height: height)
  206. } else {
  207. var xPosition: CGFloat
  208. if leftRect.isNull == false {
  209. xPosition = titleMargin + leftRect.maxX
  210. } else {
  211. xPosition = titleMargin
  212. }
  213. let width: CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX)
  214. titleRect = CGRect(x: xPosition, y: 0, width: width, height: maxHeight)
  215. }
  216. customTitleView.frame = titleRect
  217. }
  218. #else
  219. if let customTitleView = titleBarButton.customView {
  220. var leftRect = CGRect.null
  221. var rightRect = CGRect.null
  222. var isTitleBarButtonFound = false
  223. let sortedSubviews = self.subviews.sorted(by: { (view1: UIView, view2: UIView) -> Bool in
  224. if view1.frame.minX != view2.frame.minX {
  225. return view1.frame.minX < view2.frame.minX
  226. } else {
  227. return view1.frame.minY < view2.frame.minY
  228. }
  229. })
  230. for barButtonItemView in sortedSubviews {
  231. if isTitleBarButtonFound == true {
  232. rightRect = barButtonItemView.frame
  233. break
  234. } else if barButtonItemView === titleBarButton.customView {
  235. isTitleBarButtonFound = true
  236. //If it's UIToolbarButton or UIToolbarTextButton (which actually UIBarButtonItem)
  237. } else if barButtonItemView.isKind(of: UIControl.self) == true {
  238. leftRect = barButtonItemView.frame
  239. }
  240. }
  241. let titleMargin: CGFloat = 16
  242. let maxWidth: CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX)
  243. let maxHeight = self.frame.height
  244. let sizeThatFits = customTitleView.sizeThatFits(CGSize(width: maxWidth, height: maxHeight))
  245. var titleRect: CGRect
  246. if sizeThatFits.width > 0 && sizeThatFits.height > 0 {
  247. let width = min(sizeThatFits.width, maxWidth)
  248. let height = min(sizeThatFits.height, maxHeight)
  249. var xPosition: CGFloat
  250. if leftRect.isNull == false {
  251. xPosition = titleMargin + leftRect.maxX + ((maxWidth - width)/2)
  252. } else {
  253. xPosition = titleMargin
  254. }
  255. let yPosition = (maxHeight - height)/2
  256. titleRect = CGRect(x: xPosition, y: yPosition, width: width, height: height)
  257. } else {
  258. var xPosition: CGFloat
  259. if leftRect.isNull == false {
  260. xPosition = titleMargin + leftRect.maxX
  261. } else {
  262. xPosition = titleMargin
  263. }
  264. let width: CGFloat = self.frame.width - titleMargin*2 - (leftRect.isNull ? 0 : leftRect.maxX) - (rightRect.isNull ? 0 : self.frame.width - rightRect.minX)
  265. titleRect = CGRect(x: xPosition, y: 0, width: width, height: maxHeight)
  266. }
  267. customTitleView.frame = titleRect
  268. }
  269. #endif
  270. }
  271. @objc open var enableInputClicksWhenVisible: Bool {
  272. return true
  273. }
  274. deinit {
  275. items = nil
  276. privatePreviousBarButton = nil
  277. privateNextBarButton = nil
  278. privateTitleBarButton = nil
  279. privateDoneBarButton = nil
  280. privateFixedSpaceBarButton = nil
  281. }
  282. }