商城在線更新了一波薪鹦,這次更新修復(fù)了部分細(xì)微Bug池磁,同時(shí)新增商品點(diǎn)擊詳情界面分享,工具华临,屬性選擇银舱,一系列跟轉(zhuǎn)場(chǎng)動(dòng)畫(huà)有關(guān)的功能跛梗,就這次更新核偿,我分析下我項(xiàng)目中的電商詳情。
GIF圖奉上
分析步驟
-
骨架搭建
-
初步邏輯處理
-
數(shù)據(jù)和點(diǎn)擊事件處理
-
商品詳情點(diǎn)擊、選擇左腔、記錄
-
復(fù)雜邏輯處理
廢話幾句捅儒,很容易看出來(lái)CDDMall很多界面參考的是國(guó)美,詳情也不例外坊秸。點(diǎn)擊商品進(jìn)入詳情界面澎怒,很多人會(huì)一眼就看出UIScrollView或者UICollectionView為父控制器喷面,內(nèi)部添加子控制器來(lái)展示不同UITableView,UICollectionView死相,WKWebView咬像,自定義View等县昂,這個(gè)思路足夠我們用來(lái)開(kāi)發(fā)這個(gè)需求陷舅,接下來(lái)我就開(kāi)始分析具體步驟莱睁,廢話結(jié)束。那就很棒了
一创淡、骨架搭建
可以通過(guò)箭頭很直觀的看出來(lái)黑色代表根控制器琳彩,紫色是UIScrollView,灰色是UIScrollView的contentSize屬性露乏,橘色是顯示出的最終界面涂邀。我通過(guò)先在詳情界面上懶加載一個(gè)UIScrollView比勉,讓他的contentSize的width屬性等于子控制器個(gè)數(shù) * 整個(gè)屏幕的寬度驹止,然后在商品的自控制上幢哨,在懶加載一個(gè)UIScrollView嫂便,設(shè)置他的contentSize屬性的height等于他的子控制器的個(gè)數(shù) * 整個(gè)屏幕的高度
部分實(shí)現(xiàn)代碼
#pragma mark - LazyLoad
- (UIScrollView *)scrollerView
{
if (!_scrollerView) {
_scrollerView = [[UIScrollView alloc] initWithFrame:CGRectZero];
_scrollerView.frame = self.view.bounds;
_scrollerView.showsVerticalScrollIndicator = NO;
_scrollerView.showsHorizontalScrollIndicator = NO;
_scrollerView.contentSize = CGSizeMake(self.view.dc_width * self.childViewControllers.count, 0);
_scrollerView.pagingEnabled = YES;
_scrollerView.delegate = self;
[self.view addSubview:_scrollerView];
}
return _scrollerView;
}
#pragma mark - LazyLoad
- (UIScrollView *)scrollerView
{
if (!_scrollerView) {
_scrollerView = [[UIScrollView alloc] initWithFrame:CGRectZero];
_scrollerView.frame = self.view.bounds;
_scrollerView.contentSize = CGSizeMake(ScreenW, (ScreenH - 50) * 2);
_scrollerView.pagingEnabled = YES;
_scrollerView.scrollEnabled = NO;
[self.view addSubview:_scrollerView];
}
return _scrollerView;
}
添加子控制器和View
#pragma mark - 添加子控制器
-(void)setUpChildViewControllers
{
__weak typeof(self)weakSelf = self;
DCGoodBaseViewController *goodBaseVc = [[DCGoodBaseViewController alloc] init];
goodBaseVc.goodTitle = _goodTitle;
goodBaseVc.goodPrice = _goodPrice;
goodBaseVc.goodSubtitle = _goodSubtitle;
goodBaseVc.shufflingArray = _shufflingArray;
goodBaseVc.goodImageView = _goodImageView;
goodBaseVc.changeTitleBlock = ^(BOOL isChange) {
if (isChange) {
weakSelf.title = @"圖文詳情";
weakSelf.navigationItem.titleView = nil;
self.scrollerView.contentSize = CGSizeMake(self.view.dc_width, 0);
}else{
weakSelf.title = nil;
weakSelf.navigationItem.titleView = weakSelf.bgView;
self.scrollerView.contentSize = CGSizeMake(self.view.dc_width * self.childViewControllers.count, 0);
}
};
[self addChildViewController:goodBaseVc];
DCGoodParticularsViewController *goodParticularsVc = [[DCGoodParticularsViewController alloc] init];
[self addChildViewController:goodParticularsVc];
DCGoodCommentViewController *goodCommentVc = [[DCGoodCommentViewController alloc] init];
[self addChildViewController:goodCommentVc];
}
#pragma mark - 添加子控制器View
-(void)addChildViewController
{
NSInteger index = _scrollerView.contentOffset.x / _scrollerView.dc_width;
UIViewController *childVc = self.childViewControllers[index];
if (childVc.view.superview) return; //判斷添加就不用再添加了
childVc.view.frame = CGRectMake(index * _scrollerView.dc_width, 0, _scrollerView.dc_width, _scrollerView.dc_height);
[_scrollerView addSubview:childVc.view];
}
二、初步邏輯處理
導(dǎo)航欄頂部按鈕點(diǎn)擊厂画,滑動(dòng)界面頂部按鈕的相應(yīng)
#pragma mark - 頭部按鈕點(diǎn)擊
- (void)topBottonClick:(UIButton *)button
{
button.selected = !button.selected;
[_selectBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
_selectBtn = button;
__weak typeof(self)weakSelf = self;
[UIView animateWithDuration:0.25 animations:^{
weakSelf.indicatorView.dc_width = button.titleLabel.dc_width;
weakSelf.indicatorView.dc_centerX = button.dc_centerX;
}];
CGPoint offset = _scrollerView.contentOffset;
offset.x = _scrollerView.dc_width * button.tag;
[_scrollerView setContentOffset:offset animated:YES];
}
#pragma mark - <UIScrollViewDelegate>
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
{
[self addChildViewController];
}
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
NSInteger index = scrollView.contentOffset.x / scrollView.dc_width;
UIButton *button = _bgView.subviews[index];
[self topBottonClick:button];
[self addChildViewController];
}
三屎慢、數(shù)據(jù)和點(diǎn)擊事件處理
數(shù)據(jù)
數(shù)據(jù)沒(méi)什么好說(shuō)的腻惠,還是延續(xù)之前手寫(xiě)Plist欲虚,
資源數(shù)據(jù)數(shù)據(jù)Plist目錄可以到這個(gè)目錄下去看复哆,為什么能更方便的找到詳情的代碼我將整個(gè)詳情抽了出來(lái),單獨(dú)放在最外層目錄
商品詳情目錄
點(diǎn)擊事件處理
在橘色的商品唆阿,詳情,評(píng)價(jià)界面中,我們發(fā)現(xiàn)臼隔,點(diǎn)擊商品的某一個(gè)cell會(huì)跳轉(zhuǎn)到詳情或者評(píng)價(jià)界面摔握,通過(guò)發(fā)通知來(lái)實(shí)現(xiàn)的氨淌,具體代碼如下,還有包括點(diǎn)擊彈出地址的更換
#pragma mark - 滾動(dòng)到詳情頁(yè)面
- (void)scrollToDetailsPage
{
dispatch_sync(dispatch_get_global_queue(0, 0), ^{//異步發(fā)通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"scrollToDetailsPage" object:nil];
});
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { //評(píng)論Cell點(diǎn)擊
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
[[NSNotificationCenter defaultCenter]postNotificationName:@"scrollToCommentsPage" object:nil];
});
}
#pragma mark - 接受通知
- (void)acceptanceNote
{
//滾動(dòng)到詳情
__weak typeof(self)weakSlef = self;
_dcObserve = [[NSNotificationCenter defaultCenter]addObserverForName:@"scrollToDetailsPage" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
[weakSlef topBottonClick:weakSlef.bgView.subviews[1]]; //跳轉(zhuǎn)詳情
}];
_dcObserve = [[NSNotificationCenter defaultCenter]addObserverForName:@"scrollToCommentsPage" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
[weakSlef topBottonClick:weakSlef.bgView.subviews[2]]; //跳轉(zhuǎn)到評(píng)論界面
}];
}
#pragma mark - 更換地址
- (void)chageUserAdress
{
_adPickerView = [AddressPickerView shareInstance];//單例
[_adPickerView showAddressPickView];
[self.view addSubview:_adPickerView];
__weak typeof(self)weakSelf = self;
_adPickerView.block = ^(NSString *province,NSString *city,NSString *district) {
DCUserInfo *userInfo = UserInfoData;
NSString *newAdress = [NSString stringWithFormat:@"%@ %@ %@",province,city,district];
if ([userInfo.defaultAddress isEqualToString:newAdress]) {
return;
}
userInfo.defaultAddress = newAdress;
[userInfo save]; //本地?cái)?shù)據(jù)保存
[weakSelf.collectionView reloadData];
};
}
接下來(lái)是重點(diǎn),商品屬性的選擇
之前有寫(xiě)過(guò)一個(gè)簡(jiǎn)單的商品屬性選擇痰滋,沒(méi)有邏輯單純的實(shí)現(xiàn)敲街,如需請(qǐng)先參考兩種思路下的屬性選擇
四严望、商品詳情點(diǎn)擊像吻、選擇、記錄(較為復(fù)雜的邏輯包含其中)
第一步首先是彈出屬性篩選的控制器,分兩種情況:
- 第一種點(diǎn)擊請(qǐng)選擇商品屬性Cell姆涩;
- 第二種是在上一次未選擇商品的屬性情況下點(diǎn)擊右下角的加入購(gòu)物車或立即購(gòu)買(mǎi)
代碼如下
//加入購(gòu)物車或點(diǎn)擊直接購(gòu)買(mǎi)通知
_dcObj = [[NSNotificationCenter defaultCenter]addObserverForName:@"ClikAddOrBuy" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
DCFeatureSelectionViewController *dcFeaVc = [DCFeatureSelectionViewController new];
__weak typeof(self)weakSelf = self;
dcFeaVc.userChooseBlock = ^(NSMutableArray *seleArray, NSInteger num, NSInteger tag) { //第一次更新選擇的屬性
NSString *result = [NSString stringWithFormat:@"%@ %zd件",[seleArray componentsJoinedByString:@","],num];
weakSelf.cell.contentLabel.text = result;
};
if ([weakSelf.cell.leftTitleLable.text isEqual:@"已選"]) {
if ([note.userInfo[@"buttonTag"] isEqualToString:@"2"]) { //加入購(gòu)物車
}else if ([note.userInfo[@"buttonTag"] isEqualToString:@"3"]){//立即購(gòu)買(mǎi)
DCFillinOrderViewController *dcFillVc = [DCFillinOrderViewController new];
[weakSelf.navigationController pushViewController:dcFillVc animated:YES];
}
}else{
dcFeaVc.goodImageView = _goodImageView;
[self setUpAlterViewControllerWith:dcFeaVc WithDistance:ScreenH * 0.8 WithDirection:XWDrawerAnimatorDirectionBottom WithParallaxEnable:YES WithFlipEnable:YES];
}
}];
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0 && indexPath.row == 0) {
[self scrollToDetailsPage]; //滾動(dòng)到詳情頁(yè)面
}else if (indexPath.section == 2 && indexPath.row == 0) {
[self chageUserAdress]; //跟換地址
}else if (indexPath.section == 1){ //屬性選擇
DCFeatureSelectionViewController *dcFeaVc = [DCFeatureSelectionViewController new];
__weak typeof(self)weakSelf = self;
dcFeaVc.userChooseBlock = ^(NSMutableArray *seleArray, NSInteger num, NSInteger tag) { //更新選擇的屬性
if (lastSeleArray_ == seleArray && lastNum_ == num)return; //傳遞過(guò)來(lái)的數(shù)據(jù)判斷與上一次的相同則返回
NSString *result = [NSString stringWithFormat:@"%@ %zd件",[seleArray componentsJoinedByString:@","],num];
weakSelf.cell.contentLabel.text = result;
lastNum_ = num;
lastSeleArray_ = seleArray;
if (tag == 0) {
}else if(tag == 1){
DCFillinOrderViewController *dcFillVc = [DCFillinOrderViewController new];
[weakSelf.navigationController pushViewController:dcFillVc animated:YES];
}
if ([weakSelf.cell.leftTitleLable.text isEqualToString:@"已選"])return;
weakSelf.cell.leftTitleLable.text = @"已選";
};
dcFeaVc.lastNum = lastNum_;
dcFeaVc.lastSeleArray = lastSeleArray_;
dcFeaVc.goodImageView = _goodImageView;
[self setUpAlterViewControllerWith:dcFeaVc WithDistance:ScreenH * 0.8 WithDirection:XWDrawerAnimatorDirectionBottom WithParallaxEnable:YES WithFlipEnable:YES];
}
}
接下來(lái)我們到彈出屬性的控制器上览爵,我們初始化屬性數(shù)組的時(shí)候一定記得判斷上一次選擇的屬性的數(shù)組是否存在镇饮,如果有上一次用戶選擇的屬性記錄我們需要在初始化的時(shí)候储藐,將上一次的選擇產(chǎn)展示處理,即現(xiàn)在選中狀態(tài)蛛碌,包括數(shù)量辖源。
不僅僅如此,我還需要在傳遞出去的userChooseBlock中對(duì)用戶這次的已選數(shù)據(jù)和上一次選擇數(shù)據(jù)進(jìn)行判斷酝蜒,已經(jīng)選擇過(guò)程中是否已經(jīng)選擇完畢亡脑,按鈕的是否可以點(diǎn)擊判斷,進(jìn)過(guò)一層層的判斷這樣才能保證傳遞出去的數(shù)據(jù)是符合需求的奈偏。
代碼如下
首先是_lastSeleArray存在的情況下反向遍歷初始化還原上一次用戶的選擇
if (_lastSeleArray.count == 0) return;
for (NSString *str in _lastSeleArray) {//反向遍歷
for (NSInteger i = 0; i < _featureAttr.count; i++) {
for (NSInteger j = 0; j < _featureAttr[i].list.count; j++) {
if ([_featureAttr[i].list[j].infoname isEqualToString:str]) {
_featureAttr[i].list[j].isSelect = YES;
[self.collectionView reloadData];
}
}
}
}
其次是層層判斷惊来,按鈕的點(diǎn)擊和回調(diào)
#pragma mark - 底部按鈕點(diǎn)擊
- (void)buttomButtonClick:(UIButton *)button
{
if (_seleArray.count != _featureAttr.count && _lastSeleArray.count != _featureAttr.count) {//未選擇全屬性警告
[SVProgressHUD showInfoWithStatus:@"請(qǐng)選擇全屬性"];
[SVProgressHUD setDefaultStyle:SVProgressHUDStyleDark];
[SVProgressHUD dismissWithDelay:1.0];
return;
}
[self dismissFeatureViewControllerWithTag:button.tag];
}
#pragma mark - 退出當(dāng)前界面
- (void)dismissFeatureViewControllerWithTag:(NSInteger)tag
{
__weak typeof(self)weakSelf = self;
[weakSelf dismissViewControllerAnimated:YES completion:^{
if (![weakSelf.cell.chooseAttLabel.text isEqualToString:@"有貨"]) {//當(dāng)選擇全屬性才傳遞出去
if (_seleArray.count == 0) {
NSMutableArray *numArray = [NSMutableArray arrayWithArray:_lastSeleArray];
!weakSelf.userChooseBlock ? : weakSelf.userChooseBlock(numArray,num_,tag);
}else{
!weakSelf.userChooseBlock ? : weakSelf.userChooseBlock(_seleArray,num_,tag);
}
}
}];
}
最后是在我們退出選擇屬性控制的時(shí)候裁蚁,我們?cè)谀玫綌?shù)據(jù)的時(shí)候也是需要判斷的继准。情況有屬性未選擇完畢點(diǎn)擊叉叉退出界面移必,選擇完畢不存在上一次選擇數(shù)據(jù),存在上一次的選擇數(shù)據(jù)秒赤,以及這一次返回的數(shù)據(jù)和上一次是都相等憎瘸。
DCFeatureSelectionViewController *dcFeaVc = [DCFeatureSelectionViewController new];
__weak typeof(self)weakSelf = self;
dcFeaVc.userChooseBlock = ^(NSMutableArray *seleArray, NSInteger num, NSInteger tag) { //更新選擇的屬性
if (lastSeleArray_ == seleArray && lastNum_ == num)return; //傳遞過(guò)來(lái)的數(shù)據(jù)判斷與上一次的相同則返回
NSString *result = [NSString stringWithFormat:@"%@ %zd件",[seleArray componentsJoinedByString:@"幌甘,"],num];
weakSelf.cell.contentLabel.text = result;
lastNum_ = num;
lastSeleArray_ = seleArray;
if (tag == 0) {
}else if(tag == 1){
DCFillinOrderViewController *dcFillVc = [DCFillinOrderViewController new];
[weakSelf.navigationController pushViewController:dcFillVc animated:YES];
}
if ([weakSelf.cell.leftTitleLable.text isEqualToString:@"已選"])return;
weakSelf.cell.leftTitleLable.text = @"已選";
};
dcFeaVc.lastNum = lastNum_;
dcFeaVc.lastSeleArray = lastSeleArray_;
dcFeaVc.goodImageView = _goodImageView;
[self setUpAlterViewControllerWith:dcFeaVc WithDistance:ScreenH * 0.8 WithDirection:XWDrawerAnimatorDirectionBottom WithParallaxEnable:YES WithFlipEnable:YES];
更新總結(jié)
-
以上的本次更新就先說(shuō)到這里锅风,項(xiàng)目有時(shí)間仍會(huì)繼續(xù)更新,希望大家有需要的話可以去GitHub上下載Demo盆均,結(jié)合源碼看分析我覺(jué)得效果會(huì)更好漱逸。
-
項(xiàng)目寫(xiě)完我自己也很少測(cè)試,肯定會(huì)有很多不完善的地方诀黍,發(fā)現(xiàn)后會(huì)陸續(xù)完善仗处。
-
GitHub傳送門(mén)