No Description

FFPageView.swift 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. //
  2. // FFPageView.swift
  3. // FFPage
  4. //
  5. // Created by FFIB on 2017/10/19.
  6. // Copyright © 2017年 FFIB. All rights reserved.
  7. //
  8. import UIKit
  9. public protocol FFPageViewDataSource {
  10. func pageView(pageView: FFPageView, cellForItemAt index: Int) -> FFPageContentViewCell
  11. }
  12. public protocol FFPageViewDelegate {
  13. func pageViewWithClickMenu(pageView: FFPageView)
  14. }
  15. @IBDesignable
  16. public class FFPageView: UIView {
  17. private var pageMenuView = FFPageMenuView()
  18. private var pageContentView = FFPageContentView()
  19. private var needlayout = true
  20. private var rootVC: UIViewController?
  21. private var ffConstraints = [NSLayoutConstraint]()
  22. //configuratable parameter
  23. public var dataSource: FFPageViewDataSource?
  24. public var delegate: FFPageViewDelegate?
  25. var isNavigationMenu = true {
  26. willSet {
  27. if newValue != isNavigationMenu {
  28. needlayout = true
  29. setNeedsLayout()
  30. }
  31. }
  32. }
  33. public var sliderView = UIView() {
  34. willSet {
  35. if sliderView != newValue {
  36. pageMenuView.sliderView = newValue
  37. }
  38. }
  39. }
  40. public var selectedColor = UIColor.blue {
  41. willSet {
  42. if selectedColor != newValue {
  43. pageMenuView.selectedColor = newValue
  44. }
  45. }
  46. }
  47. public var normalColor = UIColor.black {
  48. willSet {
  49. if normalColor != newValue {
  50. pageMenuView.normalColor = newValue
  51. }
  52. }
  53. }
  54. public var pageMenuContentMode = FFPageMenuContentMode.scaleAspectFill {
  55. willSet {
  56. pageMenuView.pageMenuContentMode = newValue
  57. }
  58. }
  59. public var font = UIFont.systemFont(ofSize: 17) {
  60. willSet {
  61. if font != newValue {
  62. pageMenuView.font = newValue
  63. }
  64. }
  65. }
  66. public var titles = [String]() {
  67. willSet {
  68. if titles != newValue {
  69. pageMenuView.titles = newValue
  70. pageContentView.needLayout = true
  71. pageMenuView.setNeedsLayout()
  72. pageContentView.setNeedsLayout()
  73. }
  74. }
  75. }
  76. public override func layoutSubviews() {
  77. super.layoutSubviews()
  78. if needlayout {
  79. initSubViews()
  80. }
  81. }
  82. func initSubViews() {
  83. needlayout = false
  84. pageContentView.removeFromSuperview()
  85. pageMenuView.removeFromSuperview()
  86. NSLayoutConstraint.deactivate(ffConstraints)
  87. ffConstraints.removeAll()
  88. pageMenuView.pageDelegate = self
  89. pageContentView.dataSource = self
  90. pageContentView.pageDelegate = self
  91. addSubview(pageContentView)
  92. pageContentView.translatesAutoresizingMaskIntoConstraints = false
  93. pageMenuView.translatesAutoresizingMaskIntoConstraints = false
  94. let contentTopConstraint: NSLayoutConstraint
  95. var responder = self.next
  96. while responder != nil {
  97. if let targetVC = responder as? UIViewController {
  98. rootVC = targetVC
  99. break
  100. }
  101. responder = responder?.next
  102. }
  103. if #available(iOS 11.0, *) {
  104. pageContentView.contentInsetAdjustmentBehavior = .never
  105. } else {
  106. rootVC?.automaticallyAdjustsScrollViewInsets = false
  107. }
  108. switch isNavigationMenu {
  109. case false:
  110. addSubview(pageMenuView)
  111. let menuConstraints = [NSLayoutConstraint(item: pageMenuView,
  112. attribute: .top,
  113. relatedBy: .equal,
  114. toItem: self,
  115. attribute: .top,
  116. multiplier: 1,
  117. constant: 0),
  118. NSLayoutConstraint(item: pageMenuView,
  119. attribute: .leading,
  120. relatedBy: .equal,
  121. toItem: self,
  122. attribute: .leading,
  123. multiplier: 1,
  124. constant: 0),
  125. NSLayoutConstraint(item: pageMenuView,
  126. attribute: .trailing,
  127. relatedBy: .equal,
  128. toItem: self,
  129. attribute: .trailing,
  130. multiplier: 1,
  131. constant: 0),
  132. NSLayoutConstraint(item: pageMenuView,
  133. attribute: .height,
  134. relatedBy: .equal,
  135. toItem: nil,
  136. attribute: .height,
  137. multiplier: 1,
  138. constant: 44)]
  139. contentTopConstraint = NSLayoutConstraint(item: pageContentView,
  140. attribute: .top,
  141. relatedBy: .equal,
  142. toItem: pageMenuView,
  143. attribute: .bottom,
  144. multiplier: 1,
  145. constant: 0)
  146. ffConstraints += menuConstraints
  147. case true:
  148. var barContentView: UIView?
  149. if #available(iOS 11, *) {
  150. barContentView = rootVC?
  151. .navigationController?
  152. .navigationBar
  153. .subviews
  154. .filter { NSStringFromClass($0.classForCoder).contains("NavigationBarContentView") }
  155. .first
  156. } else {
  157. barContentView = rootVC?.navigationController?.navigationBar
  158. }
  159. contentTopConstraint = NSLayoutConstraint(item: pageContentView,
  160. attribute: .top,
  161. relatedBy: .equal,
  162. toItem: self,
  163. attribute: .top,
  164. multiplier: 1,
  165. constant: 0)
  166. let menuConstraints = [NSLayoutConstraint(item: pageMenuView,
  167. attribute: .top,
  168. relatedBy: .equal,
  169. toItem: barContentView,
  170. attribute: .top,
  171. multiplier: 1,
  172. constant: 0),
  173. NSLayoutConstraint(item: pageMenuView,
  174. attribute: .bottom,
  175. relatedBy: .equal,
  176. toItem: barContentView,
  177. attribute: .bottom,
  178. multiplier: 1,
  179. constant: 0),
  180. NSLayoutConstraint(item: pageMenuView,
  181. attribute: .centerX,
  182. relatedBy: .equal,
  183. toItem: barContentView,
  184. attribute: .centerX,
  185. multiplier: 1,
  186. constant: 0)]
  187. ffConstraints += menuConstraints
  188. NotificationCenter.default.addObserver(forName: NSNotification.Name.init("UINavigationController.willShow"), object: nil, queue: nil, using: { (notification) in
  189. if let userInfo = notification.userInfo,
  190. let vc = userInfo["targetController"] as? UIViewController,
  191. self.rootVC == vc {
  192. self.pageMenuView.isHidden = false
  193. } else {
  194. self.pageMenuView.isHidden = true
  195. }
  196. })
  197. }
  198. let contentConstraints = [contentTopConstraint,
  199. NSLayoutConstraint(item: pageContentView,
  200. attribute: .leading,
  201. relatedBy: .equal,
  202. toItem: self,
  203. attribute: .leading,
  204. multiplier: 1,
  205. constant: 0),
  206. NSLayoutConstraint(item: pageContentView,
  207. attribute: .trailing,
  208. relatedBy: .equal,
  209. toItem: self,
  210. attribute: .trailing,
  211. multiplier: 1,
  212. constant: 0),
  213. NSLayoutConstraint(item: pageContentView,
  214. attribute: .bottom,
  215. relatedBy: .equal,
  216. toItem: self,
  217. attribute: .bottom,
  218. multiplier: 1,
  219. constant: 0),
  220. NSLayoutConstraint(item: pageContentView,
  221. attribute: .width,
  222. relatedBy: .equal,
  223. toItem: self,
  224. attribute: .width,
  225. multiplier: 1,
  226. constant: 0)]
  227. ffConstraints += contentConstraints
  228. NSLayoutConstraint.activate(ffConstraints)
  229. }
  230. public func dequeue(with identifier: String) -> FFPageContentViewCell {
  231. return pageContentView.dequeue(with: identifier)
  232. }
  233. public func register(nib: UINib, with identifier: String) {
  234. pageContentView.register(nib: nib, with: identifier)
  235. }
  236. public func register(cell: AnyClass, with identifier: String) {
  237. pageContentView.register(cell: cell, with: identifier)
  238. }
  239. deinit {
  240. NotificationCenter.default.removeObserver(self)
  241. }
  242. }
  243. extension FFPageView: FFPageMenuViewDelegate {
  244. public func pageMenuView(pageMenuView: FFPageMenuView, didSelectItemAt index: Int) {
  245. pageContentView.scrollRectToVisible(CGRect(x: CGFloat(index) * frame.width, y: 0, width: frame.width, height: frame.height), animated: true)
  246. if let delegate = delegate {
  247. delegate.pageViewWithClickMenu(pageView: self)
  248. }
  249. }
  250. }
  251. extension FFPageView: FFPageContentViewDelegate {
  252. public func pageContentView(pageContentView: FFPageContentView, didSelectItemAt index: Int) {
  253. let offset = pageContentView.contentOffset.x / pageContentView.frame.width
  254. pageMenuView.scroll(offset: offset)
  255. }
  256. }
  257. extension FFPageView: FFPageContentViewDataSource {
  258. public func pageContentView(pageContentView: FFPageContentView, cellForItemAt index: Int) -> FFPageContentViewCell {
  259. guard let dataSource = dataSource else {
  260. fatalError("\(self) did not set up FFPageViewDataSource")
  261. }
  262. return dataSource.pageView(pageView: self, cellForItemAt: index)
  263. }
  264. public func numberOfSections(in pageContentView: FFPageContentView) -> Int {
  265. return titles.count
  266. }
  267. }
  268. extension UIViewController: UINavigationControllerDelegate {
  269. public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
  270. NotificationCenter.default.post(name: NSNotification.Name("UINavigationController.willShow"), object: nil, userInfo: ["targetController": viewController])
  271. }
  272. }