SSPage-Swift

Easy to use with UIPageViewController page left and right switch components, very easy to customize TAB and header, with default TAB

features

  • The header section can be slid, continuing with sliding ~
  • It has ground-glass penetration
  • Custom TAB, TAB suspension
  • You can customize the animation by tab-sliding percentage callbacks, such as dragging the bottom bar from the menu left to right to follow the animation
  • Custom header headers
  • Custom animations can be called back based on the percentage of head slide distance, such as head image magnification
  • Pull down, pull up refresh

The principle of partial

  1. Page left and right switching effect (similar to micro-blog, Twitter personal home page), there are a lot of open source components have this function, such as the use of more: JXPagingView.

Principle is to use multiple nested scrollView, probably then scrollView shouldRecognizeSimultaneouslyWith processing gestures conflict, The contentOffset of scrollView controls the contentOffset of other ScrollViews to achieve this effect. I think that since the VC page switch, the system automatic UIPageViewController can be implemented, but to have a headerView also need to spend some time to think about the implementation scheme. The solution I came up with was pretty simple, pageViewController on the bottom, HeaderView on the top, and then reset the content of the pageViewController child below the header, Control the location of headerView and TAB by listening on contentOffset of pageViewController’s child scrollView, but there are two difficulties:

  • In the header area you can slide the pageViewController child scrollView
  • Control the position of scrollView of each child controller, because after the current sliding scrollView position changes, the headerView also changes, and the scrollView of other child controllers should also change correspondingly.

The second problem is relatively easy to solve, but the logic is slightly complicated. The first problem is to return the current sliding scrollView in the hitTest of headerView, so that it can slide, but the click event response on the headerView needs to judge the control one by one, which is very tedious. It is also not suitable for customization of the back headerView. Is there a way to swipe once without affecting the click event? Yes, use the following code to control the action of the scrollView sliding gesture:

 weak var panGestureScrollView: UIScrollView?
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        let view = super.hitTest(point, with: event)
        if let panGestureScrollView = panGestureScrollView {
            if self.point(inside: point, with: event), let view = view {
                panGestureScrollView.panGestureRecognizer.delaysTouchesBegan = true
                if (view.gestureRecognizers ?? []).contains(panGestureScrollView.panGestureRecognizer) == false  {
                    view.addGestureRecognizer(panGestureScrollView.panGestureRecognizer)
                }
            } else {
                if (panGestureScrollView.gestureRecognizers ?? []).contains(panGestureScrollView.panGestureRecognizer) == false  {
                    panGestureScrollView.addGestureRecognizer(panGestureScrollView.panGestureRecognizer)
                }
                
            }
        }
        
        return view
    }
Copy the code

So that’s basically how it works, because it’s implemented using UIPageViewController without all the logic of nested ScrollViews

The effect





use

1. Import SSPageViewController or POD ‘sspage-Swift’

2. Inherit SSPageViewController and add child controllers

Follow SSPageChildDelegate

extension DemoViewController: SSPageChildDelegate {
    
    func childContainerView() -> UIScrollView? {
        return tableView
    }
}
Copy the code

1. The TAB is delivered by default


let viewControllers = titles.map({
                title -> SSPageChildViewController in
                let vc = DemoViewController(text: "default: \(title)")
                return vc
            })
setViewControllers(viewControllers: viewControllers, titles: titles, headerView: headerView)
Copy the code

HeaderView is an optional parameter

2. Customize TAB and Header

let viewControllers = titles.map({
                title -> SSPageChildViewController in
                let vc = DemoViewController(text: "custom: \(title)")
                return vc
            })
 let tabView = CustomTabView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 40))
 setViewControllers(viewControllers: viewControllers, tabView: tabView,  headerView: headerView)
Copy the code

The custom TAB, must implement the SSPageTabSelectedDelegate protocol and tabSelectedTrigger trigger

class CustomTabView: UIView, SSPageTabSelectedDelegate { func selectTab(index: Int) { segmentedControl.selectedSegmentIndex = index } private lazy var segmentedControl: UISegmentedControl = { let segmentedControl = UISegmentedControl(frame:.zero) return segmentedControl }() override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .white addSubview(segmentedControl) segmentedControl.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height) for index in 0.. <4 { segmentedControl.insertSegment(withTitle: "seleted\(index)", at: index, animated: true) } segmentedControl.addTarget(self, action: #selector(segmentedAction(segmented:)), for: .valueChanged) segmentedControl.selectedSegmentIndex = 0 } @objc private func segmentedAction(segmented: UISegmentedControl) { if let tabSelectedTrigger = tabSelectedTrigger { tabSelectedTrigger(segmented.selectedSegmentIndex) } } required init? (coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }Copy the code

2. Refresh

Support pull down refresh, this head slide is not elastic effect

isSupportHeaderRefresh = true 
Copy the code

2. Enlarge the head picture

Custom header, implement SSPageHeaderDelegate, control image changes in the callback method scrollDistanceFromTop

class CustomImageHeaderView: UIView, SSPageHeaderDelegate { private lazy var bgImageView: UIImageView = { let imageV = UIImageView() imageV.image = UIImage(named: "image") imageV.frame = CGRect(x: 0, y: 0, width: frame.width, height: frame.height) imageV.contentMode = .scaleAspectFill imageV.layer.masksToBounds = true return imageV }() override init(frame: CGRect) { super.init(frame: Frame) addSubview(bgImageView) let button = UIButton() button.backgroundcolor =.red button.setTitle(" click ", for: .normal) addSubview(button) button.frame = CGRect(x: 0, y: 0, width: 80, height: 40) button.center = CGPoint(x: frame.width / 2, y: frame.height / 2) button.addTarget(self, action: #selector(clickAction(button:)), for: .touchUpInside) } @objc private func clickAction(button: UIButton) {print(" click ")} func scrollDistanceFromTop(distance: CGFloat) { let scale = distance / frame.height bgImageView.transform = CGAffineTransform(scaleX: scale + 1, y: scale + 1) bgImageView.center = CGPoint(x: frame.width / 2, y: frame.height / 2 - distance / 2) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } }Copy the code

Feel good to give a star ❤️