|
@@ -1,408 +0,0 @@
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-import UIKit
|
|
|
-
|
|
|
-@objc public protocol JXPagingViewListViewDelegate: NSObjectProtocol {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func listView() -> UIView
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func listScrollView() -> UIScrollView
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func listViewDidScrollCallback(callback: @escaping (UIScrollView)->())
|
|
|
-
|
|
|
-
|
|
|
- @objc optional func listScrollViewWillResetContentOffset()
|
|
|
-
|
|
|
-
|
|
|
- @objc optional func listDidAppear()
|
|
|
-
|
|
|
-
|
|
|
- @objc optional func listDidDisappear()
|
|
|
-}
|
|
|
-
|
|
|
-@objc public protocol JXPagingViewDelegate: NSObjectProtocol {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func tableHeaderViewHeight(in pagingView: JXPagingView) -> Int
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func tableHeaderView(in pagingView: JXPagingView) -> UIView
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func heightForPinSectionHeader(in pagingView: JXPagingView) -> Int
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func viewForPinSectionHeader(in pagingView: JXPagingView) -> UIView
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func numberOfLists(in pagingView: JXPagingView) -> Int
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func pagingView(_ pagingView: JXPagingView, initListAtIndex index: Int) -> JXPagingViewListViewDelegate
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- @objc optional func mainTableViewDidScroll(_ scrollView: UIScrollView)
|
|
|
-}
|
|
|
-
|
|
|
-open class JXPagingView: UIView {
|
|
|
- public weak var delegate: JXPagingViewDelegate!
|
|
|
- open var mainTableView: JXPagingMainTableView!
|
|
|
- open var listContainerView: JXPagingListContainerView!
|
|
|
- public var validListDict = [Int:JXPagingViewListViewDelegate]()
|
|
|
- open var pinSectionHeaderVerticalOffset: CGFloat = 0
|
|
|
- public var isListHorizontalScrollEnabled = true {
|
|
|
- didSet {
|
|
|
- refreshListHorizontalScrollEnabledState()
|
|
|
- }
|
|
|
- }
|
|
|
- open var automaticallyDisplayListVerticalScrollIndicator = true
|
|
|
- public var currentScrollingListView: UIScrollView?
|
|
|
- public var currentList: JXPagingViewListViewDelegate?
|
|
|
- private var currentDeviceOrientation: UIDeviceOrientation?
|
|
|
- private var currentIndex: Int = 0
|
|
|
- private var retainedSelf: JXPagingView?
|
|
|
- private var isWillRemoveFromWindow: Bool = false
|
|
|
- private var isFirstMoveToWindow: Bool = true
|
|
|
-
|
|
|
- deinit {
|
|
|
- NotificationCenter.default.removeObserver(self)
|
|
|
- }
|
|
|
-
|
|
|
- public init(delegate: JXPagingViewDelegate) {
|
|
|
- self.delegate = delegate
|
|
|
- super.init(frame: CGRect.zero)
|
|
|
-
|
|
|
- initializeViews()
|
|
|
- }
|
|
|
-
|
|
|
- @available(*, unavailable)
|
|
|
- required public init?(coder aDecoder: NSCoder) {
|
|
|
- fatalError("init(coder:) has not been implemented")
|
|
|
- }
|
|
|
-
|
|
|
- open func initializeViews(){
|
|
|
- mainTableView = JXPagingMainTableView(frame: CGRect.zero, style: .plain)
|
|
|
- mainTableView.showsVerticalScrollIndicator = false
|
|
|
- mainTableView.showsHorizontalScrollIndicator = false
|
|
|
- mainTableView.separatorStyle = .none
|
|
|
- mainTableView.dataSource = self
|
|
|
- mainTableView.delegate = self
|
|
|
- mainTableView.scrollsToTop = false
|
|
|
- mainTableView.tableHeaderView = self.delegate.tableHeaderView(in: self)
|
|
|
- mainTableView.register(UITableViewCell.classForCoder(), forCellReuseIdentifier: "cell")
|
|
|
- addSubview(mainTableView)
|
|
|
-
|
|
|
- if #available(iOS 11.0, *) {
|
|
|
- mainTableView.contentInsetAdjustmentBehavior = .never
|
|
|
- }
|
|
|
-
|
|
|
- listContainerView = JXPagingListContainerView(delegate: self)
|
|
|
- listContainerView.mainTableView = mainTableView
|
|
|
-
|
|
|
- refreshListHorizontalScrollEnabledState()
|
|
|
-
|
|
|
- self.currentDeviceOrientation = UIDevice.current.orientation
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(deviceOrientationDidChange(notification:)), name: UIDevice.orientationDidChangeNotification, object: nil)
|
|
|
- }
|
|
|
-
|
|
|
- open override func willMove(toWindow newWindow: UIWindow?) {
|
|
|
- if self.isFirstMoveToWindow {
|
|
|
-
|
|
|
- self.isFirstMoveToWindow = false
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- if newWindow == nil {
|
|
|
- self.isWillRemoveFromWindow = true
|
|
|
-
|
|
|
- self.retainedSelf = self
|
|
|
- self.perform(#selector(currentListDidDisappear), with: nil, afterDelay: 0.02)
|
|
|
- }else {
|
|
|
- if self.isWillRemoveFromWindow {
|
|
|
- self.isWillRemoveFromWindow = false
|
|
|
- NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(currentListDidDisappear), object: nil)
|
|
|
- }else {
|
|
|
- self.currentListDidAppear()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- override open func layoutSubviews() {
|
|
|
- super.layoutSubviews()
|
|
|
-
|
|
|
- mainTableView.frame = self.bounds
|
|
|
- }
|
|
|
-
|
|
|
- open func reloadData() {
|
|
|
- self.currentList = nil
|
|
|
- self.currentScrollingListView = nil
|
|
|
-
|
|
|
- for list in validListDict.values {
|
|
|
- list.listView().removeFromSuperview()
|
|
|
- }
|
|
|
- validListDict.removeAll()
|
|
|
-
|
|
|
- self.mainTableView.tableHeaderView = self.delegate.tableHeaderView(in: self)
|
|
|
- self.mainTableView.reloadData()
|
|
|
- self.listContainerView.reloadData()
|
|
|
- }
|
|
|
-
|
|
|
- open func preferredProcessListViewDidScroll(scrollView: UIScrollView) {
|
|
|
- if (self.mainTableView.contentOffset.y < getMainTableViewMaxContentOffsetY()) {
|
|
|
-
|
|
|
- self.currentList?.listScrollViewWillResetContentOffset?()
|
|
|
- currentScrollingListView!.contentOffset = CGPoint.zero
|
|
|
- if automaticallyDisplayListVerticalScrollIndicator {
|
|
|
- currentScrollingListView!.showsVerticalScrollIndicator = false
|
|
|
- }
|
|
|
- } else {
|
|
|
-
|
|
|
- self.mainTableView.contentOffset = CGPoint(x: 0, y: getMainTableViewMaxContentOffsetY())
|
|
|
- if automaticallyDisplayListVerticalScrollIndicator {
|
|
|
- currentScrollingListView!.showsVerticalScrollIndicator = true
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- open func preferredProcessMainTableViewDidScroll(_ scrollView: UIScrollView) {
|
|
|
- if (self.currentScrollingListView != nil && self.currentScrollingListView!.contentOffset.y > 0) {
|
|
|
-
|
|
|
- self.mainTableView.contentOffset = CGPoint(x: 0, y: getMainTableViewMaxContentOffsetY())
|
|
|
- }
|
|
|
-
|
|
|
- if (self.mainTableView.contentOffset.y < getMainTableViewMaxContentOffsetY()) {
|
|
|
-
|
|
|
- for list in self.validListDict.values {
|
|
|
- list.listScrollViewWillResetContentOffset?()
|
|
|
- list.listScrollView().contentOffset = CGPoint.zero
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- if scrollView.contentOffset.y > getMainTableViewMaxContentOffsetY() && self.currentScrollingListView?.contentOffset.y == 0 {
|
|
|
-
|
|
|
- self.mainTableView.contentOffset = CGPoint(x: 0, y: getMainTableViewMaxContentOffsetY())
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func refreshListHorizontalScrollEnabledState() {
|
|
|
- listContainerView.collectionView.isScrollEnabled = isListHorizontalScrollEnabled
|
|
|
- }
|
|
|
-
|
|
|
- func getMainTableViewMaxContentOffsetY() -> CGFloat {
|
|
|
- return CGFloat(self.delegate.tableHeaderViewHeight(in: self)) - pinSectionHeaderVerticalOffset
|
|
|
- }
|
|
|
-
|
|
|
- func getPinSectionHeaderHeight() -> CGFloat {
|
|
|
- return CGFloat(self.delegate.heightForPinSectionHeader(in: self))
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- func listViewDidScroll(scrollView: UIScrollView) {
|
|
|
- self.currentScrollingListView = scrollView
|
|
|
-
|
|
|
- preferredProcessListViewDidScroll(scrollView: scrollView)
|
|
|
- }
|
|
|
-
|
|
|
- @objc func deviceOrientationDidChange(notification: Notification) {
|
|
|
- if self.currentDeviceOrientation != UIDevice.current.orientation {
|
|
|
- self.currentDeviceOrientation = UIDevice.current.orientation
|
|
|
-
|
|
|
- mainTableView.reloadData()
|
|
|
- listContainerView.deviceOrientationDidChanged()
|
|
|
- listContainerView.reloadData()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @objc func currentListDidDisappear() {
|
|
|
- let list = self.validListDict[self.currentIndex]
|
|
|
- list?.listDidDisappear?()
|
|
|
- self.isWillRemoveFromWindow = false
|
|
|
- self.retainedSelf = nil
|
|
|
- }
|
|
|
-
|
|
|
- func currentListDidAppear() {
|
|
|
- self.listDidAppear(index: self.currentIndex)
|
|
|
- }
|
|
|
-
|
|
|
- func listDidAppear(index: Int) {
|
|
|
- let count = self.delegate.numberOfLists(in: self)
|
|
|
- if count <= 0 || index >= count {
|
|
|
- return
|
|
|
- }
|
|
|
- self.currentIndex = index
|
|
|
- let list = self.validListDict[index]
|
|
|
- list?.listDidAppear?()
|
|
|
- }
|
|
|
-
|
|
|
- func listDidDisappear(index: Int) {
|
|
|
- let count = self.delegate.numberOfLists(in: self)
|
|
|
- if count <= 0 || index >= count {
|
|
|
- return
|
|
|
- }
|
|
|
- self.currentIndex = index
|
|
|
- let list = self.validListDict[index]
|
|
|
- list?.listDidDisappear?()
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-extension JXPagingView: UITableViewDataSource, UITableViewDelegate {
|
|
|
- public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
|
|
- return 1
|
|
|
- }
|
|
|
-
|
|
|
- public func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
|
|
- return self.bounds.height - getPinSectionHeaderHeight() - pinSectionHeaderVerticalOffset
|
|
|
- }
|
|
|
-
|
|
|
- public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
|
|
- let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
|
|
|
- for subview in cell.contentView.subviews {
|
|
|
- subview.removeFromSuperview()
|
|
|
- }
|
|
|
- listContainerView.frame = cell.contentView.bounds
|
|
|
- cell.contentView.addSubview(listContainerView)
|
|
|
- return cell
|
|
|
- }
|
|
|
-
|
|
|
- public func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
|
|
|
- return getPinSectionHeaderHeight()
|
|
|
- }
|
|
|
-
|
|
|
- public func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
|
|
|
- return self.delegate.viewForPinSectionHeader(in: self)
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- public func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
|
|
|
- return 1
|
|
|
- }
|
|
|
-
|
|
|
- public func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
|
|
|
- let footerView = UIView(frame: CGRect.zero)
|
|
|
- footerView.backgroundColor = UIColor.clear
|
|
|
- return footerView
|
|
|
- }
|
|
|
-
|
|
|
- public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
|
-
|
|
|
- if scrollView.isTracking && isListHorizontalScrollEnabled {
|
|
|
- self.listContainerView.collectionView.isScrollEnabled = false
|
|
|
- }
|
|
|
-
|
|
|
- if scrollView.contentOffset.y < pinSectionHeaderVerticalOffset && scrollView.contentOffset.y >= 0 {
|
|
|
-
|
|
|
- scrollView.contentInset = UIEdgeInsets(top: -scrollView.contentOffset.y, left: 0, bottom: 0, right: 0)
|
|
|
- }else if scrollView.contentOffset.y > pinSectionHeaderVerticalOffset {
|
|
|
-
|
|
|
- scrollView.contentInset = UIEdgeInsets(top: pinSectionHeaderVerticalOffset, left: 0, bottom: 0, right: 0)
|
|
|
- }
|
|
|
-
|
|
|
- preferredProcessMainTableViewDidScroll(scrollView)
|
|
|
-
|
|
|
- self.delegate.mainTableViewDidScroll?(scrollView)
|
|
|
- }
|
|
|
-
|
|
|
- public func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
|
|
|
- if isListHorizontalScrollEnabled {
|
|
|
- self.listContainerView.collectionView.isScrollEnabled = true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
|
|
- if isListHorizontalScrollEnabled {
|
|
|
- self.listContainerView.collectionView.isScrollEnabled = true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
|
|
|
- if isListHorizontalScrollEnabled {
|
|
|
- self.listContainerView.collectionView.isScrollEnabled = true
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-extension JXPagingView: JXPagingListContainerViewDelegate {
|
|
|
- public func numberOfRows(in listContainerView: JXPagingListContainerView) -> Int {
|
|
|
- return self.delegate.numberOfLists(in: self)
|
|
|
- }
|
|
|
- public func listContainerView(_ listContainerView: JXPagingListContainerView, viewForListInRow row: Int) -> UIView {
|
|
|
- var list = validListDict[row]
|
|
|
- if list == nil {
|
|
|
- list = self.delegate.pagingView(self, initListAtIndex: row)
|
|
|
- list?.listViewDidScrollCallback {[weak self, weak list] (scrollView) in
|
|
|
- self?.currentList = list
|
|
|
- self?.listViewDidScroll(scrollView: scrollView)
|
|
|
- }
|
|
|
- validListDict[row] = list!
|
|
|
- }
|
|
|
- for listItem in validListDict.values {
|
|
|
- if listItem === list {
|
|
|
- listItem.listScrollView().scrollsToTop = true
|
|
|
- }else {
|
|
|
- listItem.listScrollView().scrollsToTop = false
|
|
|
- }
|
|
|
- }
|
|
|
- return list!.listView()
|
|
|
- }
|
|
|
-
|
|
|
- public func listContainerView(_ listContainerView: JXPagingListContainerView, willDisplayCellAt row: Int) {
|
|
|
- self.listDidAppear(index: row)
|
|
|
- self.currentScrollingListView = validListDict[row]?.listScrollView()
|
|
|
- }
|
|
|
-
|
|
|
- public func listContainerView(_ listContainerView: JXPagingListContainerView, didEndDisplayingCellAt row: Int) {
|
|
|
- self.listDidDisappear(index: row)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|