cdts/xdts-ios 3/TreeHole/Code/Utility/MTPage/MTPageView.m
2023-07-27 09:20:00 +08:00

491 lines
17 KiB
Objective-C

//
// MTPageView.m
// MTPage
//
// Created by ko1o on 2019/5/6.
// Copyright © 2019年 ko1o. All rights reserved.
//
#import "MTPageView.h"
@interface MTPageParetViewController : UIViewController
@end
@implementation MTPageParetViewController
- (BOOL)shouldAutomaticallyForwardAppearanceMethods
{
return NO;
}
@end
@interface MTPageItem : NSObject
@property (nonatomic, strong) id itemModel;
@property (nonatomic, strong) UIView *menuItemView;
@property (nonatomic, strong) UIView *menuItemContentView;
@property (nonatomic, strong) UIViewController *viewController;
@end
@implementation MTPageItem
- (void)dealloc
{
[self.menuItemView removeFromSuperview];
[self.menuItemContentView removeFromSuperview];
[self.viewController willMoveToParentViewController:nil];
[self.viewController removeFromParentViewController];
self.viewController = nil;
}
@end
@interface MTPageView () <UIScrollViewDelegate>
@property (nonatomic, strong) MTPageConfig *config;
@property (nonatomic, strong) NSMutableArray <MTPageItem *> *pageItems;
@property (nonatomic, copy) void(^itemDidSelectBlock)(id, NSInteger);
@property (nonatomic, assign) NSInteger currentIndex;
@property (nonatomic, strong) MTPageParetViewController *parentViewController;
@property (nonatomic, strong) UIView *sliderBar;
@property (nonatomic, strong) UIView *bottomLineView;
@property (nonatomic, strong) UIActivityIndicatorView *loadingView;
@property (nonatomic, copy) void (^setupConfigBlock)(MTPageConfig * _Nonnull);
@end
@implementation MTPageView
- (UIViewController *)pageViewController
{
return _parentViewController;
}
- (instancetype)init
{
if (self = [super init]) {
[self setupConfig];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
[self setupConfig];
}
return self;
}
- (UIScrollView *)menuContentScrollView
{
if (!_menuContentScrollView) {
_menuContentScrollView = [[UIScrollView alloc] initWithFrame:CGRectZero];
}
return _menuContentScrollView;
}
- (MTPageConfig *)pageConfig
{
return _config;
}
- (void)startLoading
{
self.pageContentScrollView.hidden = YES;
self.menuContentScrollView.hidden = YES;
self.loadingView.alpha = 1.0;
}
- (BOOL)isLoading
{
return self.loadingView.alpha >= 0.99;
}
- (void)endLoadingWithItemModels:(NSArray *)itemModels
{
self.pageContentScrollView.hidden = NO;
self.menuContentScrollView.hidden = NO;
self.loadingView.alpha = 0.0;
if (self.setupConfigBlock) {
self.setupConfigBlock(self.config);
}
[self setupUIWithItemModels:itemModels];
[self relayoutUI];
}
- (void)setupConfig
{
self.config = [[MTPageConfig alloc] init];
self.parentViewController = [[MTPageParetViewController alloc] init];
[self addSubview:self.parentViewController.view];
self.loadingView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
self.loadingView.alpha = 0.0;
self.loadingView.centerX = self.width * 0.5;
self.loadingView.y = 20;
[self.loadingView startAnimating];
self.menuContentScrollView.frame = CGRectMake(0, 0, self.config.menuContentWidth, 0);
UIView *bottomView = [[UIView alloc] init];
bottomView.width = SCREEN_WIDTH;
bottomView.height = 0.7;
bottomView.backgroundColor = COLOR_WITH_RGB(0xeeeeee);
[self.menuContentScrollView addSubview:bottomView];
self.bottomLineView = bottomView;
self.menuContentScrollView.clipsToBounds = NO;
self.sliderBar = [[UIView alloc] init];
self.sliderBar.width = 30.0;
self.sliderBar.height = 2.0;
self.sliderBar.layer.cornerRadius = 1;
self.sliderBar.backgroundColor = COLOR_WITH_RGB(0x333333);
[self.menuContentScrollView addSubview:self.sliderBar];
[self addSubview:self.loadingView];
}
- (void)setFrame:(CGRect)frame
{
[super setFrame:frame];
self.parentViewController.view.frame = self.bounds;
self.loadingView.centerX = self.width * 0.5;
_pageContentScrollView.frame = CGRectMake(0, CGRectGetMaxY(_menuContentScrollView.frame), self.frame.size.width, self.frame.size.height - CGRectGetMaxY(_menuContentScrollView.frame));
_pageContentScrollView.bounces = NO;
for (MTPageItem *item in [self.pageItems mutableCopy]) {
CGRect pageViewFrame = item.viewController.view.frame;
pageViewFrame.size = _pageContentScrollView.bounds.size;
item.viewController.view.frame = pageViewFrame;
}
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self.menuContentScrollView addSubview:self.sliderBar];
self.sliderBar.bottom = self.menuContentScrollView.height - self.menuContentScrollView.contentInset.bottom - 10;
self.sliderBar.hidden = self.pageItems.count == 0;
if (self.config.didLayoutSubviewsBlock) {
self.config.didLayoutSubviewsBlock(self, self.pageContentScrollView, self.menuContentScrollView, self.sliderBar);
}
if (self.pageItems.count > self.currentIndex) {
MTPageItem *pageItem = self.pageItems[self.currentIndex];
self.sliderBar.centerX = pageItem.menuItemView.superview.centerX;
self.sliderBar.bottom = self.menuContentScrollView.height - self.menuContentScrollView.contentInset.top - self.menuContentScrollView.contentInset.bottom + 1;
[self.menuContentScrollView.superview insertSubview:self.bottomLineView belowSubview:self.menuContentScrollView];
self.bottomLineView.y = self.menuContentScrollView.height;
self.bottomLineView.hidden = !self.config.showBottomLine;
self.menuContentScrollView.clipsToBounds = YES;
self.pageContentScrollView.height = self.height - self.pageContentScrollView.y;
}
}
- (void)setupUIWithItemModels:(NSArray *)itemModels
{
MTPageConfig *config = self.config;
self.currentIndex = -1;
_pageItems = [NSMutableArray array];
_menuContentScrollView.contentInset = config.menuContentInset;
_menuContentScrollView.showsHorizontalScrollIndicator = NO;
[self addSubview:_menuContentScrollView];
_pageContentScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(_menuContentScrollView.frame), self.frame.size.width, self.frame.size.height - CGRectGetMaxY(_menuContentScrollView.frame))];
_pageContentScrollView.pagingEnabled = YES;
_pageContentScrollView.showsHorizontalScrollIndicator = NO;
_pageContentScrollView.scrollEnabled = self.config.pageScrollEnable;
_pageContentScrollView.delegate = self;
[self.parentViewController.view addSubview:_pageContentScrollView];
[_menuContentScrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[_pageContentScrollView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
for (id itemModel in itemModels) {
[self addItemWithModel:itemModel needRelayout:NO];
}
UIView *lastMenuItemContentView = self.pageItems.lastObject.menuItemContentView;
UIView *lastPageView = self.pageItems.lastObject.viewController.view;
_menuContentScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastMenuItemContentView.frame), 0);
_pageContentScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastPageView.frame), 0);
[self selectItemAtIndex:config.defaultPageIndex animated:NO];
}
- (void)viewWillAppear:(BOOL)animated
{
UIViewController *currentVc = self.pageItems[self.currentIndex].viewController;
[currentVc viewWillAppear:animated];
}
- (void)relayoutUI
{
UIView *lastMenuItemContentView = nil;
UIView *lastPageView = nil;
for (int i = 0; i < self.pageItems.count; i++) {
MTPageItem *pageItem = self.pageItems[i];
CGRect menuItemContentViewFrame = pageItem.menuItemContentView.frame;
menuItemContentViewFrame.origin = CGPointMake(CGRectGetMaxX(lastMenuItemContentView.frame) + (lastMenuItemContentView ? self.config.menuItemSpacing : 0 ), menuItemContentViewFrame.origin.y);
pageItem.menuItemContentView.frame = menuItemContentViewFrame;
pageItem.menuItemContentView.tag = i;
lastMenuItemContentView = pageItem.menuItemContentView;
UIView *pageSubview = pageItem.viewController.view;
CGRect pageSubviewFrame = pageSubview.frame;
pageSubviewFrame.origin = CGPointMake(CGRectGetMaxX(lastPageView.frame), pageSubviewFrame.origin.y);
pageSubview.frame = pageSubviewFrame;
lastPageView = pageSubview;
}
_menuContentScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastMenuItemContentView.frame), 0);
_pageContentScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastPageView.frame), 0);
if (self.currentIndex >= self.pageItems.count && self.pageItems.count > 0) {
self.currentIndex = self.pageItems.count - 1;
}
[self selectItemAtIndex:self.currentIndex];
}
- (void)menuItemDidTap:(UITapGestureRecognizer *)gr
{
UIView *menuItemContentView = gr.view;
[self selectItemAtIndex:menuItemContentView.tag];
}
+ (instancetype)pageViewWithItemModels:(NSArray *)itemModels
setupConfig:(void (^)(MTPageConfig * _Nonnull))setupConfigBlock
itemDidSelect:(void (^)(id _Nonnull, NSInteger))itemDidSelectBlock
{
MTPageView *pageView = [[self alloc] init];
pageView.itemDidSelectBlock = itemDidSelectBlock;
pageView.setupConfigBlock = setupConfigBlock;
if (setupConfigBlock) {
setupConfigBlock(pageView.config);
}
pageView.frame = CGRectMake(0, 0, pageView.config.pageWidth, pageView.config.pageHeight);
if (itemModels.count > 0) {
[pageView setupUIWithItemModels:itemModels];
}
return pageView;
}
- (void)selectItemAtIndex:(NSInteger)index animated:(BOOL)animated
{
if (index == self.currentIndex || index < 0 || index >= self.pageItems.count) return;
/// 选择菜单
[self selectedMenuItemAtIndex:index animated:animated];
/// 滚动到指定pageview
[self scrollToPageViewAtIndex:index animated:animated];
}
/// 选中指定下标
- (void)selectItemAtIndex:(NSInteger)index
{
BOOL animated = self.config.switchAnimated;
[self selectItemAtIndex:index animated:animated];
}
- (UIViewController *)currentPageViewController
{
return self.currentIndex < self.parentViewController.childViewControllers.count ? self.parentViewController.childViewControllers[self.currentIndex] : nil;
}
- (void)selectedMenuItemAtIndex:(NSInteger)index animated:(BOOL)animated
{
NSTimeInterval animatedDuration = animated * self.config.animatedDuration;
MTPageItem *lastSelectPageItem = nil;
if (self.currentIndex >= 0 && self.currentIndex < self.pageItems.count) {
lastSelectPageItem = self.pageItems[self.currentIndex];
}
void(^changeMenuItemStateBlock)(void) = ^ {
MTPageItem *pageItem = self.pageItems[index];
[UIView animateWithDuration:animatedDuration animations:^{
if (self.config.itemStateChangedBlock) {
self.config.itemStateChangedBlock(lastSelectPageItem.menuItemView, lastSelectPageItem.itemModel, MTPageMenuStateNormal);
self.config.itemStateChangedBlock(pageItem.menuItemView, pageItem.itemModel, MTPageMenuStateSelected);
}
self.sliderBar.centerX = pageItem.menuItemView.superview.centerX;
}];
if (self.itemDidSelectBlock) {
self.itemDidSelectBlock(pageItem.itemModel, index);
}
CGFloat maxX = CGRectGetMaxX(pageItem.menuItemView.superview.frame) + self.menuContentScrollView.contentInset.right;
CGFloat minX = CGRectGetMinX(pageItem.menuItemView.superview.frame) - self.menuContentScrollView.contentInset.left;
if (minX < self.menuContentScrollView.contentOffset.x) {
[self.menuContentScrollView setContentOffset:CGPointMake(minX, self.menuContentScrollView.contentOffset.y) animated:YES];
}
if (maxX > self.menuContentScrollView.contentOffset.x + self.menuContentScrollView.frame.size.width) {
[self.menuContentScrollView setContentOffset:CGPointMake(maxX - self.menuContentScrollView.frame.size.width, self.menuContentScrollView.contentOffset.y) animated:YES];
}
};
[UIView animateWithDuration:animatedDuration animations:^{
changeMenuItemStateBlock();
}];
self.currentIndex = index;
}
- (void)scrollToPageViewAtIndex:(NSInteger)index animated:(BOOL)animated
{
[UIView animateWithDuration:self.config.animatedDuration * animated animations:^{
[self.pageContentScrollView setContentOffset:CGPointMake(self.pageContentScrollView.frame.size.width * index, 0) animated:animated];
}];
[self handlePageViewDiappearOrAppearWhenSelectIndex:index animated:animated];
}
- (void)handlePageViewDiappearOrAppearWhenSelectIndex:(NSInteger)index animated:(BOOL)animated
{
UIViewController *willDisappearVc = self.pageItems[self.currentIndex].viewController;
UIViewController *willAppearVc = self.pageItems[index].viewController;
[willDisappearVc viewWillDisappear:YES];
[willAppearVc viewWillAppear:YES];
[UIView animateWithDuration:self.config.animatedDuration * animated animations:^{
} completion:^(BOOL finished) {
[willDisappearVc viewDidDisappear:YES];
[willAppearVc viewDidAppear:YES];
}];
}
/// 对换下标
- (void)exchangeItemAtIndex:(NSInteger)index1 withItemAtIndex:(NSInteger)index2
{
[self.pageItems exchangeObjectAtIndex:index1 withObjectAtIndex:index2];
[self relayoutUI];
}
/// 移除下标
- (void)removeItemAtIndex:(NSInteger)index
{
[self.pageItems removeObjectAtIndex:index];
[self relayoutUI];
}
- (void)addItemWithModel:(id)itemModel
{
[self addItemWithModel:itemModel needRelayout:YES];
}
- (void)addItemWithModel:(id)itemModel needRelayout:(BOOL)needRelayout
{
MTPageConfig *config = self.config;
UIView *lastMenuItemContentView = self.pageItems.lastObject.menuItemContentView;
UIView *lastPageView = self.pageItems.lastObject.viewController.view;
MTPageItem *pageItem = [[MTPageItem alloc] init];
pageItem.itemModel = itemModel;
if (config.setupMenuItemContentViewBlock) {
UIView *menuItemContentView = [[UIView alloc] initWithFrame:CGRectMake(CGRectGetMaxX(lastMenuItemContentView.frame) + (lastMenuItemContentView ? config.menuItemSpacing : 0), 0, config.menuItemSize.width, config.menuItemSize.height)];
UIView *menuItemView = config.setupMenuItemContentViewBlock(menuItemContentView, itemModel);
if (!CGSizeEqualToSize(config.menuItemSize, CGSizeZero)) { // 设置item大小
CGRect frame = menuItemView.frame;
frame.size = config.menuItemSize;
menuItemView.frame = frame;
} else {
CGRect frame = menuItemContentView.frame;
frame.size = menuItemView.frame.size;
menuItemContentView.frame = frame;
}
CGRect menuContentScrollViewFrame = _menuContentScrollView.frame;
menuContentScrollViewFrame.size = CGSizeMake(config.menuContentWidth, menuItemContentView.height + config.menuContentInset.top + config.menuContentInset.bottom);
_menuContentScrollView.frame = menuContentScrollViewFrame;
[menuItemContentView addSubview:menuItemView];
[_menuContentScrollView addSubview:menuItemContentView];
pageItem.menuItemView = menuItemView;
pageItem.menuItemContentView = menuItemContentView;
menuItemContentView.tag = self.pageItems.count;
[menuItemContentView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(menuItemDidTap:)]];
menuItemView.centerY = menuItemContentView.height * 0.5;
lastMenuItemContentView = menuItemContentView;
}
if (config.setupPageContentViewBlock) {
UIViewController *vc = config.setupPageContentViewBlock(_pageContentScrollView, itemModel);
pageItem.viewController = vc;
[self.parentViewController addChildViewController:vc];
UIView *view = vc.view;
view.frame = CGRectMake(CGRectGetMaxX(lastPageView.frame), 0, _pageContentScrollView.frame.size.width, _pageContentScrollView.frame.size.height);
[_pageContentScrollView addSubview:view];
[vc didMoveToParentViewController:self.parentViewController];
lastPageView = view;
}
if (self.config.itemStateChangedBlock) {
self.config.itemStateChangedBlock(pageItem.menuItemView, itemModel, MTPageMenuStateNormal);
}
[self.pageItems addObject:pageItem];
_menuContentScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastMenuItemContentView.frame), 0);
_pageContentScrollView.contentSize = CGSizeMake(CGRectGetMaxX(lastPageView.frame), 0);
if (needRelayout) {
[self relayoutUI];
}
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
{
NSInteger index = (*targetContentOffset).x / scrollView.frame.size.width;
if (index != self.currentIndex) {
[self selectedMenuItemAtIndex:index animated:self.config.switchAnimated];
[self handlePageViewDiappearOrAppearWhenSelectIndex:index animated:self.config.switchAnimated];
}
}
@end