你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS高仿城覓項目(開發思路和代碼)

iOS高仿城覓項目(開發思路和代碼)

編輯:IOS開發基礎

前言

很早前就想和大家分享一些真正項目的開發思路和流程,一直沒能鼓起勇氣寫,畢竟這不是一件輕松的事情,一個月前,公司的項目上線,鼓起勇氣利用每天的休息時間寫了這個半成品的項目,基本的UI與邏輯都打通了,剩下的細節需要時間修改,由於下周工作有新需求需要開發,可能很長一段時間沒精力來寫了,本想把所有的功能都實現,並且修改掉發現的bug後再給大家學習的,由於工作的情況,可能會延緩2個月左右的日子(後續我會把功能陸續實現),代碼我傳到github上了,相信讀者會發現很多的不足之處,希望大家可以自行嘗試修改一些bug和實現剩余的部分功能,說實話一直是每天晚上8點寫到凌晨3點左右,因為有些朋友不希望看見過多的三方庫,所以基本非常耗時的需求我都盡力自己封裝的,當然我寫的有很多不足之處,希望大家可以指出不足之處,共同進步,由於時間比較匆忙,我的code一次review都沒,望大家包涵,項目使用OC寫的,算是對OC的一個紀念吧。等忙完這段時間,我會再用swift來寫一個新的項目分享給大家,喜歡的朋友可以繼續關注我的博客,我會第一時間將新項目到博客上

這篇博客是配合代碼來寫的,大部分的圖片都是我自己摳下來的,數據也是我直接寫的假數據(嘗試抓了下接口都是加密的- -)在代碼中基本每一步我都有詳細的注釋,唠叨的有點多,下面就先展示下我仿的這個項目吧,有興趣的朋友可以下一下原版的app參照一下(寫的過程中app更新了一點新功能)

項目展示,由於沒有數據,所以所有的cell顯示的都是我自己寫的數據。

1.gif
抽屜

2.gif
首頁部分效果

3.gif

首頁效果

4.gif

部分效果

5.gif

發現

6.gif

消息

7.gif
搜索

8.gif

設置

9.gif

模糊效果

10.jpg

代碼注釋展示

11.jpg

代碼注釋展示

還有很多細節就不一一展示了,大家將代碼運行下自己查看即可。由於內容比較多,我就按功能模塊來介紹給大家了。

首先是左邊抽屜的效果以及點擊按鈕切換控制器

  • 實際這裡相當於自己定義一個和系統UITabBarController差不多功能的控件,在最底層有一個控制器(後面稱之為主控制器),將左邊的按鈕view添加到主控制器的view上,創建好右邊有所的控制器(首頁,發現,消息,設置...)並且將每個右邊控制器包裝一個導航控制器,將導航控制器按序添加給主控制器做子控制器,默認情況下將首頁的導航控制器的view添加給主控制器的view子控件,根據左邊按鈕的點擊事件通過代理方法.移除舊控制器view從父視圖,將新的view添加到主視圖的view具體代碼如下,用一個臨時屬性之前選中的控制器

  //暫時先做沒有登陸的情況的點擊
 WNXNavigationController *newNC = self.childViewControllers[toIndex];
 if (toIndex == WNXleftButtonTypeIcon) {
     newNC = self.childViewControllers[fromIndex];
 }
 //移除舊的控制器view
 WNXNavigationController *oldNC = self.childViewControllers[fromIndex];
 [oldNC.view removeFromSuperview];
 //添加新的控制器view
 [self.view addSubview:newNC.view];
 newNC.view.transform = oldNC.view.transform;
 self.showViewController = newNC.childViewControllers[0];

這樣就完成了切換控制器

  • 抽屜的效果是通過給控制器view做形變動畫完成的,由於每個導航控制器的功能一樣,這裡抽取了共同的特點封裝了一個基類導航控制器,點擊左邊的按鈕完成抽屜效果

  • 拖動手勢是給主控制器添加一個UIPanGestureRecognizer手勢,根據拖動的距離計算出該停留在哪裡的位置,這裡判斷很多,具體實現我在代碼中每一步都有注釋,參照代碼即可

首頁

  • 首頁就是一個tableView就可以搞定,tableView的headView顏色和數據服務器會給返回,給每個headView添加一個點擊手勢,點擊push到下一個控制器,導航條的顏色會和前一個headView的顏色一樣,,這裡由於我之前設置了導航控制器的主題

[UINavigationBar appearanceWhenContainedIn:self, nil]
  • 所以不可以直接設置導航條的顏色了 這裡我嘗試了設置navigationBar的背景色,設置navigationBar的setTintColor:

  設置navigationBar.layer的背景色 以及根據顏色畫出navigationBar的背景圖片4種辦法都無法達到原生的效果

  最後采用將navigationBar隱藏,自己放一個View了冒充導航條來解決這個問題

發現

  • 這個頁面是一個UICollectionView,裡面有兩組數據,每一組都一個一個headView,需要注意的就是cell的點擊事件,這裡注意了下官方的做法是不論點擊了cell的哪個位置,都會使cell內部的button進入高亮狀態,這裡需要用到事件的響應鏈,在cell的內部攔截整個cell的點擊事件都交給按鈕來做,具體代碼如下

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
  /*  攔截事件響應者,不論觸發了cell中的哪個控件都交給iconButton來響應 */
  // 1.判斷當前控件能否接收事件
  if (self.userInteractionEnabled == NO || self.hidden == YES || self.alpha <= 0.01) return nil;
  // 2. 判斷點在不在當前控件
  if ([self pointInside:point withEvent:event] == NO) return nil;
  return self.iconButton;
}

但這裡需要注意這樣攔截cell的點擊事件,在collectionView的cell被點擊觸發didDeselectItemAtIndexPath:就不會被觸發了,我的解決方法是在點擊button時通過代理方法傳給collectionView,外部在通過知道點擊了那個cell,push到下一個控制器,並將cell的model賦值給下一個控制器

登陸(登陸只用了微信和新浪登陸,不涉及到注冊就非常簡單,這些只需去官網下來登陸和分享的sdk集成進來即可,我一般使用友盟平台,包括崩潰統計,三方登陸,分享,用戶分析等等)

消息

  • 一樣這裡也是tabelView,這裡我個人的邏輯是將所有的消息歸檔到本地,每次點擊刪除一條,將本地的數據刪除一條,重新歸檔

   當點擊刪除全部的時候,就清空本地的歸檔數據,下次接受的服務器的數據在重新寫入

   因為是模擬的數據,為了保障每次進來都有數據,就沒有實現歸檔解檔的操作,所以每次刪除後重新進入會再次有數據

  • 這裡記錄編輯按鈕的狀態,讀取本地是否有未讀消息數組的個數,如果有就顯示編輯按鈕,記錄編輯按鈕的狀態,如果是選中狀態就隱藏>圖片,顯示刪除按鈕,點擊刪除按鈕就將本地的數據數組刪除掉並且刷新tableView,這裡用的是刪除動畫,需要注意刪除的順序

  [self.datas removeObjectAtIndex:indexPath.row];
  [self.tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
      [self.tableView reloadData];
  });
  • 底部的刪除全部按鈕完全交給編輯按鈕來控制.

搜索

12.gif
搜索


  • 這個也需要持久化存儲功能,每次頁面彈出後,先從本地讀取用戶歷史搜索的數據,用戶每次刪除或者新輸入收縮內容時也直接寫入到本地的caches文件中

  • 這裡需要提一下關於熱門按鈕的布局,因為熱門的文字長度不一樣,但每次只有4個按鈕,在xib中先將按鈕的位置約束好,不過寬度的約束需要倆個,一個是>= 和<= 然後根據服務器返回的實際長度在設置按鈕title時,計算出每個按鈕的真實寬度,根據真實寬度算出間距是多少,重新布局一次按鈕的位置

(void)setHotDatas:(NSMutableArray *)hotDatas
{
    _hotDatas = hotDatas;
    //判斷是長度是否是4,開發中可以這樣寫 應該服務器返回幾條數據就賦值多少,而不是固定的寫死數據,
    //萬一服務器返回的數據有錯誤,會造成用戶直接閃退的,有
    //時在某些不是很重要的東西無法確定返回的是否正確,建議用
    //@try    @catch來處理,
    //即便返回的數據有誤,也可以讓用戶繼續別的操作,
    //而不會在無關緊要的小細節上造成閃退
    if (hotDatas.count == 4) {
        [self.hotButton1 setTitle:hotDatas[1] forState:UIControlStateNormal];
        [self.hotButton2 setTitle:hotDatas[0] forState:UIControlStateNormal];
        [self.hotButton3 setTitle:hotDatas[2] forState:UIControlStateNormal];
        [self.hotButton4 setTitle:hotDatas[3] forState:UIControlStateNormal];
    }
    [self layoutIfNeeded];
    //算出間距
    CGFloat margin = (WNXAppWidth - 40 -
    self.hotButton1.bounds.size.width -
    self.hotButton2.bounds.size.width -
    self.hotButton3.bounds.size.width -
    self.hotButton3.bounds.size.width) / 3;
    //更新約束
    [self.hotButton2 updateConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.hotButton1.right).offset(margin);
    }];
    [self.hotButton3 updateConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.hotButton2.right).offset(margin);
    }];
    [self.hotButton4 updateConstraints:^(MASConstraintMaker *make) {
        make.left.equalTo(self.hotButton3.right).offset(margin);
    }];
}

模糊效果

13.gif

模糊效果

  • 這裡由於圖片的質量被壓縮的太厲害,實際的效果還不錯,這個功能是iOS8新開放的接口,有倆個圖片特效可以使用,一個是模糊blur,一個是顏色疊加(類似於PS中圖片疊加的效果,不過就3種效果)

詳情頁

15.gif
詳情頁展示

  • 這個頁面挺坑的,需要注意的細節太多,也是我耗時最久的頁面,誠然目前bug依舊不少

  • 這個頁面的層級關系很重要,需要重點注意

  • 首先是導航條,這個咋一看好像是導航條有個漸隱漸現的動畫,我的做法是在頂部放了一個高度為64的view,根據tableView的偏移量計算出view的透明度,但是透明度只是1或者0,頂部的scrollView裡面裝的imageView,根據服務器返回的圖片地址個數,設置他的展示內容大小,並且在整一個scrollView最上面添加一個和導航條一樣顏色的view,用它來做出向上推出現綠色的效果,並且根據底部scrollview的偏移計算拉伸的大小,我這裡拉伸的大小不是很准確,感覺需要將錨點釘在最頂端。

  • 然後是中間切換tableView的view(後面就叫它選擇view),要實現能像headView一樣,卡在導航條下面的效果,這裡因為沒有導航條,並且在切換tableView時候不會帶走選擇view,所以只能將他放到和頂部的view在同一個層級中,同樣根據底部scrollView的contentOffset.y計算他的位置,當偏移量超過頂部的64時,就停留在那,不超過時就回到頂部view的下面,這裡的計算我加了很多的注釋,怕計算的朋友也會看的懂的,大概是這樣

  -(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
    if (scrollView == self.rmdTableView || scrollView == self.infoTableView) {//說明是tableView在滾動
        //記錄當前展示的是那個tableView
        self.showingTableView = (UITableView *)scrollView;
        //記錄出上一次滑動的距離,因為是在tableView的contentInset中偏移的ScrollHeadViewHeight,所以都得加回來
        CGFloat offsetY = scrollView.contentOffset.y;
        CGFloat seleOffsetY = offsetY - self.scrollY;
        self.scrollY = offsetY;
        //修改頂部的scrollHeadView位置 並且通知scrollHeadView內的控件也修改位置
        CGRect headRect = self.topView.frame;
        headRect.origin.y -= seleOffsetY;
        self.topView.frame = headRect;
        //根據偏移量算出alpha的值,漸隱,當偏移量大於-180開始計算消失的值
        CGFloat startF = -180;
        //初始的偏移量Y值為 頂部倆個控件的高度
        CGFloat initY = SelectViewHeight + ScrollHeadViewHeight;
        //缺少的那一段漸變Y值
        CGFloat lackY = initY + startF;
        //自定義導航條高度
        CGFloat naviH = 64;
        //漸隱alpha值
        CGFloat alphaScaleHide = 1 - (offsetY + initY- lackY) / (initY- naviH - SelectViewHeight - lackY);
        //漸現alph值
        CGFloat alphaScaleShow = (offsetY + initY - lackY) /  (initY - naviH - SelectViewHeight - lackY) ;
        if (alphaScaleShow >= 0.98) {
            //顯示導航條
            [UIView animateWithDuration:0.04 animations:^{
                self.naviView.alpha = 1;
            }];
        } else {
            self.naviView.alpha = 0;
        }
        self.topScrollView.naviView.alpha = alphaScaleShow;
        self.subTitleLabel.alpha = alphaScaleHide;
        self.smallImageView.alpha = alphaScaleHide;
        /* 這段代碼很有深意啊。。最開始是直接用偏移量算的,但是回來的時候速度比較快時偏移量會偏度很大
         然後就悲劇了。換了好多方法。。最後才開竅T——T,這一段我會在blog裡面詳細描述我用的各種錯誤的方法
         用了KVO監聽偏移量的值,切換了selectView的父控件,切換tableview的headView。。。
         */
        if (offsetY >= -(naviH + SelectViewHeight)) {
            self.selectView.frame = CGRectMake(0, naviH, WNXAppWidth, SelectViewHeight);
        } else {
            self.selectView.frame = CGRectMake(0, CGRectGetMaxY(self.topView.frame), WNXAppWidth, SelectViewHeight);
        }
        CGFloat scaleTopView = 1 - (offsetY + SelectViewHeight + ScrollHeadViewHeight) / 100;
        scaleTopView = scaleTopView > 1 ? scaleTopView : 1;
        //算出頭部的變形 這裡的動畫不是很准確,好的動畫是一點一點試出來了  這裡可能還需要配合錨點來進行動畫,關於這種動畫我會在以後單開一個項目配合blog來講解的 這裡這就不細調了
        CGAffineTransform transform = CGAffineTransformMakeScale(scaleTopView, scaleTopView );
        CGFloat ty = (scaleTopView - 1) * ScrollHeadViewHeight;
        self.topView.transform = CGAffineTransformTranslate(transform, 0, -ty * 0.2);
        //記錄selectViewY軸的偏移量,這個是用來計算每次切換tableView,讓新出來的tableView總是在頭部用的,
        //現在腦子有點迷糊 算不出來了。。凌晨2.57分~
        CGFloat selectViewOffsetY = self.selectView.frame.origin.y - ScrollHeadViewHeight;
        if (selectViewOffsetY != -ScrollHeadViewHeight && selectViewOffsetY = (0.5 + index)) {
            [self.selectView lineToIndex:index + 1];
        } else if (seleOffsetX < 0 && offsetX / WNXAppWidth <= (0.5 + index)) {
            [self.selectView lineToIndex:index];
        }
    }
}
  • 下面是一個scrollView上添加了3個tabelView,根據服務器返回的數據判斷顯示多少個,這裡就指顯示了倆個,並且第二個頁面還沒有來得及做

這些就是這個項目的大體思路,當然還有很多很多的細節都在代碼中,第一次嘗試將思路寫出來,感覺有很多不足,本應該每寫完一個功能就總結一下,而我是在發布的晚上回頭總結的,有很多當時的思路不是很清晰了...以後我會改善的,大家有什麼意見可以直接留言,我看到會一一回復的!

請直接打開WNXHuntForCity.xcworkspace

16.jpg

打開

而不要打開WNXHuntForCity.xcodeproj

附上代碼的下載地址

希望大家可以點一下右上角star,大家的支持與鼓勵是我繼續分享的動力^_^,歡迎大家多提意見和交流

我的微博鏈接

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved