前言
iOS开发中关于电商平台的首页需要做的精细一点,才能吸引用户,因此app首页部分做了一个顶部banner+各种分类item,滚动有悬停的分栏列表的效果。
效果
大概思路
- 顶部的banner 是一个普通的视图
- 整体
- 使用一个tableView,向下偏移一个banner和item距离。如下
- 分栏列表
- 是嵌套多个ViewController完成的
上代码
主视图
- HomeViewController.m
#import "HomeViewController.h"
#import "HomeMainView.h"
#import "HomeBannerBottomModel.h"
#import "HomeSubBaseViewController.h"
#import "BestSaleViewController.h"//畅销产品
#import "NewProductViewController.h"//本月新品
#import "PromotSaleViewController.h"//本月促销
#import "DiscountSaleViewController.h"//临期折扣
@interface HomeViewController () <HomeMainViewDelegate>
@property(nonatomic, strong) HomeMainView *homeMainView;
@property(nonatomic, strong) NSMutableArray *categoryArray;
@property(nonatomic, strong) NSMutableArray *titleArray;
@end
@implementation HomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setNavigationHidden:YES];
self.view.backgroundColor = [UIColor colorWithRed:241/255.0 green:240/255.0 blue:240/255.0 alpha:1];
[self setHomeMainViewUI];
}
- (void)setHomeMainViewUI {
[self.view addSubview:self.homeMainView];
__weak typeof(self) weakSelf = self;
[self.homeMainView setBlock:^(BOOL canScroll) {
[weakSelf subTabViewCanScroll:canScroll];
}];
[self.homeMainView setBannerBottomItem:[HomeBannerBottomModel getHomeBottomViewData]];
}
- (HomeMainView *)homeMainView {
if(_homeMainView == nil) {
_homeMainView = [[HomeMainView alloc]initWithFrame:self.view.bounds withVC:self withVCarr:self.categoryArray titleArr:self.titleArray];
_homeMainView.delegate = self;
}
return _homeMainView;
}
- (NSMutableArray *)titleArray {
if (_titleArray == nil) {
_titleArray = [NSMutableArray arrayWithObjects:@"category1",@"category2",@"category3",@"category4", nil];
}
return _titleArray;
}
- (NSMutableArray *)categoryArray {
if (_categoryArray == nil) {
_categoryArray = [NSMutableArray array];
BestSaleViewController *bestSaleViewController = [[BestSaleViewController alloc] init];
[bestSaleViewController setType:self.titleArray[0]];
[_categoryArray addObject:bestSaleViewController];
NewProductViewController *newProductViewController = [[NewProductViewController alloc] init];
[newProductViewController setType:self.titleArray[1]];
[_categoryArray addObject:newProductViewController];
PromotSaleViewController *promotSaleViewController = [[PromotSaleViewController alloc] init];
[promotSaleViewController setType:self.titleArray[2]];
[_categoryArray addObject:promotSaleViewController];
DiscountSaleViewController *discountSaleViewController = [[DiscountSaleViewController alloc] init];
[discountSaleViewController setType:self.titleArray[3]];
[_categoryArray addObject:discountSaleViewController];
}
return _categoryArray;
}
- (void)subTabViewCanScroll:(BOOL)canScroll {
for (HomeSubBaseViewController *subViewController in self.categoryArray) {
[subViewController setTableViewCanscroll:canScroll];
if (!canScroll) {
[subViewController setTableViewConset:CGPointZero];
}
}
}
#pragma mark - HomeMainViewDelegate
- (void)clickBottomCollectionViewItem:(NSIndexPath *)index {
//葡萄酒,啤酒,饮料,矿泉水
}
- (void)bannerClickImage:(NSIndexPath *)index {
//滚动图片的点击
}
- (void)beginClickSearch:(UITextField *)textField {
//开始搜索
}
- (void)locationClick {
//地理位置的点击
}
- (void)shareClick {
//分享的点击
}
- (void)segmentIndex:(NSInteger)index {
}
@end
- HomeMainView.h
#import <UIKit/UIKit.h>
@class HomeBannerBottomModel;
@protocol HomeMainViewDelegate <NSObject>
@optional
- (void)clickBottomCollectionViewItem:(NSIndexPath *)index;
- (void)bannerClickImage:(NSIndexPath *)index;
- (void)beginClickSearch:(UITextField *)textField;
- (void)locationClick;
- (void)shareClick;
- (void)segmentIndex:(NSInteger)index;
@end
@interface HomeMainView : UIView <HomeMainViewDelegate>
@property(nonatomic, weak) id<HomeMainViewDelegate> delegate;
@property(nonatomic, copy) void(^block)(BOOL canScroll);
- (instancetype)initWithFrame:(CGRect)frame withVC:(UIViewController *)vc withVCarr:(NSMutableArray *)vcarr titleArr:(NSMutableArray *)titleArr;
- (void)setBannerBottomItem:(NSMutableArray<HomeBannerBottomModel *> *)arr;
@end
- HomeMainView.m
#import "HomeMainView.h"
#import "HomeTopNaviView.h"
#import "BannerView.h"
#import "SegmentView.h"
#import "SegMainScrollView.h"
#import "BaseTableView.h"
#import "HomeHeaderRefresh.h"
#import "HomeBannerBottomView.h"
#import "HomeBannerBottomModel.h"
#define BANNER_HEIGHT LYWidth(300)
#define BANNERBOTTOM LYWidth(131)
static NSString *const kHomeMainCell = @"kHomeMainCell";
@interface HomeMainView () <UITableViewDelegate,UITableViewDataSource,HomeBannerBottomViewDelegate,HomeTopNaviViewDelegate>
@property(nonatomic, strong) UIViewController *viewController;
@property(nonatomic, strong) HomeTopNaviView *homeTopNaviView;
@property(nonatomic, strong) HomeBannerBottomView *homeBannerBottomView;
@property(nonatomic, strong) BaseTableView *tableView;
@property(nonatomic, strong) SegmentView *segView;
@property(nonatomic, assign) BOOL canScroll;
@property(nonatomic, strong) NSMutableArray *segArray;
@property(nonatomic, strong) NSMutableArray *titleArray;
@end
@implementation HomeMainView
- (instancetype)initWithFrame:(CGRect)frame withVC:(UIViewController *)vc withVCarr:(NSMutableArray *)vcarr titleArr:(NSMutableArray *)titleArr {
if(self = [super initWithFrame:frame]) {
self.viewController = vc;
self.segArray = vcarr;
self.titleArray = titleArr;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(notification) name:@"tabNoti" object:nil];
self.canScroll = YES;
[self setSubViews];
// UISwipeGestureRecognizer *swipeGestureRecognizer = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(swipeGestureRecognizer:)];
// [self addGestureRecognizer:swipeGestureRecognizer];
}
return self;
}
- (void)notification {
self.canScroll = YES;
if(self.block) {
self.block(NO);
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)setSubViews {
[self addSubview:self.tableView];
//banner图
NSArray *images = @[@"top_banner",@"top_banner",@"top_banner"];
__weak typeof(self) weakSelf = self;
[BannerView showBannerWithFrame:CGRectMake(0, - BANNER_HEIGHT - BANNERBOTTOM, SCREEN_WIDTH, BANNER_HEIGHT) images:images superView:self.tableView tapBlock:^(NSInteger index) {
//NSLog(@"点击了第%ld张",index);
[weakSelf performSelector:@selector(bannerImageClick:) withObject:[NSIndexPath indexPathWithIndex:index]];
}];
[self.tableView addSubview:self.homeBannerBottomView];
[self addSubview:self.homeTopNaviView];
}
- (HomeTopNaviView *)homeTopNaviView {
if(_homeTopNaviView == nil) {
_homeTopNaviView = [[HomeTopNaviView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, LYWidth(98))];//LYWidth(98)UI长度
_homeTopNaviView.delegate = self;
}
return _homeTopNaviView;
}
- (HomeBannerBottomView *)homeBannerBottomView {
if(_homeBannerBottomView == nil) {
_homeBannerBottomView = [[HomeBannerBottomView alloc]initWithFrame:CGRectMake(0, - BANNERBOTTOM, SCREEN_WIDTH, BANNERBOTTOM)];
_homeBannerBottomView.delegate = self;
}
return _homeBannerBottomView;
}
- (SegmentView *)segView {
//分栏控制器
if(_segView == nil) {
CGRect frame = CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);//40
_segView = [[SegmentView alloc]initWithFrame:frame items:self.titleArray pageView:self.segArray parentVC:self.viewController];
_segView.firstSelectPage = 0;
__weak typeof(self) weakSelf = self;
_segView.segmentBlock = ^(NSInteger selectIndex) {
//NSLog(@"%@",self.titleArray[selectIndex]);
if(weakSelf.delegate && [weakSelf.delegate respondsToSelector:@selector(segmentIndex:)]) {
[weakSelf.delegate segmentIndex:selectIndex];
}
};
}
return _segView;
}
#pragma mark - TableViewController
- (BaseTableView *)tableView {
if (_tableView == nil) {
_tableView = [[BaseTableView alloc]initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT) style:UITableViewStylePlain];
if (@available(iOS 11.0,*)) {
_tableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
self.viewController.automaticallyAdjustsScrollViewInsets = NO;
#pragma clang diagnostic pop
}
_tableView.backgroundColor = self.superview.backgroundColor;
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.estimatedRowHeight = 0;
_tableView.contentInset = UIEdgeInsetsMake(BANNER_HEIGHT + BANNERBOTTOM, 0, 0, 0);
__weak typeof(self) weakSelf = self;
_tableView.mj_header = [HomeHeaderRefresh headerWithRefreshingBlock:^{
[weakSelf endRefresh];
}];
[_tableView.mj_header beginRefreshing];
}
return _tableView;
}
- (void)endRefresh {
[self.tableView.mj_header endRefreshing];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return SCREEN_HEIGHT;
}
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kHomeMainCell];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kHomeMainCell];
}
[cell.contentView addSubview:self.segView];
return cell;
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat naviH = TOP_AND_SYSTEM_HEIGHT;
if (scrollView.contentOffset.y > -naviH) {
scrollView.contentOffset = CGPointMake(0, - naviH);
if (self.canScroll) {
self.canScroll = NO;
if(self.block) {
self.block(YES);
}
}
} else {
if (!self.canScroll) {
scrollView.contentOffset = CGPointMake(0, - naviH);
}
}
CGFloat alpha = (scrollView.contentOffset.y + BANNER_HEIGHT + BANNERBOTTOM) / (BANNERBOTTOM + BANNER_HEIGHT - naviH);
[self.homeTopNaviView setHomeTopNaviViewAlpha:alpha];//topNavi的变化
}
#pragma mark - HomeTopNaviViewDelegate
- (void)rightBtnBackClick {
[self.tableView setContentOffset:CGPointMake(0, - BANNER_HEIGHT - BANNERBOTTOM) animated:YES];
self.canScroll = YES;
if(self.block) {
self.block(NO);
}
//self.segView.scrollview.scrollEnabled = YES;
}
#pragma mark - swipeGestureRecognizer
//- (void)swipeGestureRecognizer:(UISwipeGestureRecognizer *)tap {
// if(tap.direction == UISwipeGestureRecognizerDirectionRight && !self.canScroll && self.segView.index == 0) {
// self.segView.scrollview.scrollEnabled = NO;
// [self rightBtnBackClick];
// } else {
// return;
// }
//}
#pragma mark - setsetBannerBottomItemData
- (void)setBannerBottomItem:(NSMutableArray<HomeBannerBottomModel *> *)arr {
[self.homeBannerBottomView setHomeBannerBottomModel:arr];
}
#pragma mark - HomeBannerBottomViewDelegate
- (void)clickCollectionViewItem:(NSIndexPath *)index {
if(_delegate && [_delegate respondsToSelector:@selector(clickBottomCollectionViewItem:)]) {
[_delegate clickBottomCollectionViewItem:index];
}
}
- (void)bannerImageClick:(NSIndexPath *)index {
if(_delegate && [_delegate respondsToSelector:@selector(bannerClickImage:)]) {
[_delegate bannerClickImage:index];
}
}
- (void)homeTopNaviViewSearchChange:(UITextField *)textField {
if(_delegate && [_delegate respondsToSelector:@selector(beginClickSearch:)]) {
[_delegate beginClickSearch:textField];
}
}
- (void)homeTopNaviViewLocationClick {
if(_delegate && [_delegate respondsToSelector:@selector(locationClick)]) {
[_delegate locationClick];
}
}
- (void)homeTopNaviViewShareClick {
if(_delegate && [_delegate respondsToSelector:@selector(shareClick)]) {
[_delegate shareClick];
}
}
@end
- 顶部往下滑动的渐变效果
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat naviH = TOP_AND_SYSTEM_HEIGHT;
if (scrollView.contentOffset.y > -naviH) {
scrollView.contentOffset = CGPointMake(0, - naviH);
if (self.canScroll) {
self.canScroll = NO;
if(self.block) {
self.block(YES);
}
}
} else {
if (!self.canScroll) {
scrollView.contentOffset = CGPointMake(0, - naviH);
}
}
CGFloat alpha = (scrollView.contentOffset.y + BANNER_HEIGHT + BANNERBOTTOM) / (BANNERBOTTOM + BANNER_HEIGHT - naviH);
[self.homeTopNaviView setHomeTopNaviViewAlpha:alpha];//topNavi的变化
}
主要问题
- 两个tableView的手势冲突问题,解决办法就是在向下滚动到item+banner的距离的时候,禁止底部tableView滑动的手势,开启子控制器的滑动手势。
- 解决的代码 BaseTableView.m
#import "BaseTableView.h"
@interface BaseTableView () <UIGestureRecognizerDelegate>
@end
@implementation BaseTableView
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
//YES 识别两个手势
return YES;
}
@end