iOS高仿App源碼:10天時間純代碼打造高仿優質《內涵段子》

winxp 8年前發布 | 11K 次閱讀 iOS開發 移動開發

Github 地址 https://github.com/Charlesyaoxin/NeiHanDuanZI

介紹:

花了兩周閑余時間模仿了一下今日頭條旗下的iOS端app內涵段子,如果喜歡的話請給個star。(8.30-9.11)

這個項目是用OC編寫,如果有的朋友已經下載下來看了這個項目, 就會意識到這個項目沒有一個storyboard或者是nib,不是因為不喜歡用storyboard或者nib,而是因為一直以來就想用純代碼寫個項目,(好遠大的夢想。。開玩笑的。。),但是項目是寫出來的,光想不做不寫是不行的,所以我就開始我的”內涵之旅“了。

日志:

8.30號:沒怎么做東西,就是搭建了項目的架構,拉入了之前經常用的一些工具類,宏定義等等。

8.30主要事項:UITabbarController+UINavigationController項目架構組建。

部分代碼

// 添加子控制器

  • (void)addChildViewControllerWithClassname:(NSString *)classname
                                imagename:(NSString *)imagename
                                    title:(NSString *)title {    UIViewController *vc = [[NSClassFromString(classname) alloc] init];
    
    NHBaseNavigationViewController nav = [[NHBaseNavigationViewController alloc] initWithRootViewController:vc]; nav.tabBarItem.title = title; nav.tabBarItem.image = [UIImage imageNamed:imagename]; nav.tabBarItem.selectedImage = [[UIImage imageNamed:[imagename stringByAppendingString:@"_press"]] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; [self addChildViewController:nav]; }</code></pre>

    8.31號:開始在8.30建的類上面填充內容,首頁,這個最復雜的界面。

    搭建類似于今日頭條首頁的架構。開始抓接口,添加接口的公共參數,完善請求基類。還有幾個展示的列表頁的編寫,由簡單入難,有助于在開發中培養自信心。

    /** 鏈接/
    @property (nonatomic, copy) NSString nh_url;
    /** 默認GET/
    @property (nonatomic, assign) BOOL nh_isPost;
    /* 圖片數組/
    @property (nonatomic, strong) NSArray *nh_imageArray;

/* 構造方法/

  • (instancetype)nh_request;
  • (instancetype)nh_requestWithUrl:(NSString *)nh_url;
  • (instancetype)nh_requestWithUrl:(NSString *)nh_url isPost:(BOOL)nh_isPost;
  • (instancetype)nh_requestWithUrl:(NSString *)nh_url isPost:(BOOL)nh_isPost delegate:(id )nh_delegate;

/* 開始請求,如果設置了代理,不需要block回調/

  • (void)nh_sendRequest; /* 開始請求,沒有設置代理,或者設置了代理,需要block回調,block回調優先級高于代理/
  • (void)nh_sendRequestWithCompletion:(NHAPIDicCompletion)completion;</code></pre>

    9.1號, 控制器和cell以及普通文本圖片數據的展示,以及發布界面的視圖封裝。

    9.2 - 9.4 首頁的回調處理以及發現界面

    typedef NS_ENUM(NSUInteger, NHHomeTableViewCellItemType) {
      / 點贊*/
      NHHomeTableViewCellItemTypeLike = 1,
      //
      NHHomeTableViewCellItemTypeDontLike,
      /** 評論/
      NHHomeTableViewCellItemTypeComment,
      / 分享*/
      NHHomeTableViewCellItemTypeShare
    }; 
    @class NHHomeTableViewCellFrame , NHHomeTableViewCell, NHDiscoverSearchCommonCellFrame, NHNeiHanUserInfoModel;
    @protocol NHHomeTableViewCellDelegate / 點擊瀏覽大圖*/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell didClickImageView:(UIImageView )imageView currentIndex:(NSInteger)currentIndex urls:(NSArray )urls; /** 播放視頻/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell didClickVideoWithVideoUrl:(NSString )videoUrl videoCover:(NHBaseImageView )baseImageView; /** 分類/
  • (void)homeTableViewCellDidClickCategory:(NHHomeTableViewCell )cell; /** 個人中心/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell gotoPersonalCenterWithUserInfo:(NHNeiHanUserInfoModel )userInfoModel; /* 點擊底部item/
  • (void)homeTableViewCell:(NHHomeTableViewCell *)cell didClickItemWithType:(NHHomeTableViewCellItemType)itemType;

@optional /* 點擊關注/

  • (void)homeTableViewCellDidClickAttention:(NHHomeTableViewCell )cell; /** 刪除/
  • (void)homeTableViewCellDidClickClose:(NHHomeTableViewCell *)cell; @end @interface NHHomeTableViewCell : NHBaseTableViewCell

/ 代理*/ @property (nonatomic, weak) id delegate; / 首頁cellFrame模型/ @property (nonatomic, strong) NHHomeTableViewCellFrame cellFrame; / 搜索cellFrame模型/ @property (nonatomic, strong) NHDiscoverSearchCommonCellFrame searchCellFrame; / 用來判斷是否有刪除按鈕*/ @property (nonatomic, assign) BOOL isFromHomeController;

/* 判斷是否在詳情頁/

  • (void)setCellFrame:(NHHomeTableViewCellFrame )cellFrame isDetail:(BOOL)isDetail; /** 設置關鍵字/
  • (void)setSearchCellFrame:(NHDiscoverSearchCommonCellFrame )searchCellFrame keyWord:(NSString )keyWord; /* 點贊/
  • (void)didDigg; /*/
  • (void)didBury;</code></pre>

    9.5 - 9.7審核界面的邏輯處理和動畫處理,以及發現界面的輪播圖和自定義pageControl

    - (void)setCurrentIndex:(NSInteger)currentIndex {
      _currentIndex = currentIndex;
      UIBezierPath *path = [UIBezierPath bezierPath];
      // 設置選中layer的動畫

    CGFloat delta = self.width - self.numberOfItems self.pageWidth + (self.numberOfItems - 1) self.pageSpace - 15; [path moveToPoint:CGPointMake(currentIndex self.pageWidth + currentIndex self.pageSpace + delta, 5)]; [path addLineToPoint:CGPointMake((currentIndex + 1) self.pageWidth + currentIndex self.pageSpace + delta , 5)];

    // path(平移動畫) CGFloat duration = 1.0; CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.duration = duration; animation.fromValue = (bridge id _Nullable)(self.prePath.CGPath); animation.toValue = (bridge id _Nullable)(path.CGPath); [self.selectedLayer addAnimation:animation forKey:@""];

    self.prePath = path; }

  • (void)setNumberOfItems:(NSInteger)numberOfItems { _numberOfItems = numberOfItems; if (self.pageWidth numberOfItems + self.pageSpace (numberOfItems - 1) > self.frame.size.width) {

      self.pageWidth = (self.frame.size.width - self.pageSpace * (numberOfItems - 1)) / numberOfItems;
    

    } CGFloat originX = 0; UIBezierPath *path = [UIBezierPath bezierPath];

    // 內容充不滿,需要靠右邊對齊 CGFloat delta = self.width - numberOfItems self.pageWidth + (numberOfItems - 1) self.pageSpace - 15; for (int i = 0; i < numberOfItems; i++) {

      originX = i * self.pageSpace + self.pageWidth * i + delta;
      [path moveToPoint:CGPointMake(originX, 5)];
      [path addLineToPoint:CGPointMake(originX + self.pageWidth, 5)];
      path.lineWidth = 5;
      if (i == 0) {
          self.prePath = path;
          self.selectedLayer.path = self.prePath.CGPath;
      }
    

    } self.showPageLayer.path = path.CGPath; }</code></pre>

    9.8 - 9.9,搜索界面的邏輯處理

    個人中心內容的填充,部分公共空數據界面視圖的處理

    9.10 視頻的播放和一些地方的修修補補

    9.11部分動畫效果的完善,例如點贊和踩,關注等。。以及簡單的測試。9.11晚上編寫博文上傳Github。

    @interface NHCustomCommonEmptyView : UIView
    @property (nonatomic, weak) UIImageView topTipImageView;
    @property (nonatomic, weak) UILabel firstL;
    @property (nonatomic, weak) UILabel *secondL;

  • (instancetype)initWithTitle:(NSString *)title

                secondTitle:(NSString *)secondTitle
                   iconname:(NSString *)iconname;
  • (instancetype)initWithAttributedTitle:(NSMutableAttributedString *)attributedTitle
                secondAttributedTitle:(NSMutableAttributedString *)secondAttributedTitle
                             iconname:(NSString *)iconname;
  • (void)showInView:(UIView *)view;

@end</code></pre>

主要實現的功能如下:

首頁 : 包括點贊、踩、分享、收藏,復制鏈接,視頻的播放,上拉下拉,評論列表,關注列表

首頁:

要點處理:將請求到的列表數據,轉化為模型數組,然后計算出模型所對應的frame數組,這樣做的好處是防止CellForHeight會計算多次,缺點是計算量大, 需要耐心。

利用視圖的drawRect方法來達到滾動條滑動的時候的穿透效果,封裝分享視圖,見NHHomeShareView ,封裝帶有高斯模糊效果的自定義彈窗,與系統的UIAlertView相差無幾,效果更佳。

評論列表:利于YYLabel和NSAttributeString,將@的用戶的名字高亮,加以點擊事件。

分享: 封裝分享管理類,配置友盟的appKey和UrlScheme等一系列必要操作。

圖片瀏覽器,根據數據展示布局九宮格視圖,然后利于自定義的NHBaseImgeView,將網絡圖片的請求處理邏輯全部放到該類中,還記得當SDWebimage的方法加上sd_開頭的時候,我們吃過的虧么?

Gif圖的處理,封裝一個Gif視圖,繼承自UIImageView,然后頂部加載loading。

iOS高仿App源碼:10天時間純代碼打造高仿優質《內涵段子》

發現:輪播,熱吧列表,推薦的關注用戶列表,訂閱列表,搜索,附近的人,附近的人的篩選,

發現“

要點處理:利用UICollectionview實現無限滾動輪播視圖,利于貝塞爾曲線自定義pageControl,類似于系統的UIPageControl,當改變當前索引的時候,曲線改變,設置layer的動畫。

附近的人:思路:當app啟動的時候先請求一次定位信息,如果請求到了將經緯度保存,然后如果進入附近的人重新定位,獲取最新的經緯度,獲取附近的人列表,封裝篩選視圖,根據性別篩選附近的人。

搜索:自定義搜索框,如果業務邏輯比較深的話,用系統的UISearchBar就不太現實了,需要讓搜索框變得變得高度可定制化。搜索關鍵字,將搜索結果的文本轉化為富文本,自定義多種不同類型的cell,然后顯示數據,處理業務邏輯。要點在于,搜索的時候需要同時并發調用三個接口,搜索用戶、動態還有熱吧.

這時候處理單個界面的多個并發網絡請求用到了dispatch_group 想了解GCD可點擊此鏈接 , 當然,如果你的項目使用的RAC,那么這個dispatch_group,就可以摒棄了。

審核:舉報,喜歡和不喜歡,手動左滑刷新,利用貝塞爾曲線和CAShaperLayer加載視圖動畫

審核

處理:可以右滑來查看新的內涵段子動畫,詳情見下面Gif圖。利于UICollectionview進行頁面展示,自定義UICollectionviewFlowLayout布局。

封裝舉報底部視圖

利于UIWebView加載Gif圖,這里的處理不是很好

封裝一個帶有loading進度條的時候,loading進度條的實現使用了CAShapeLayer和白塞爾曲線以及基本動畫,詳情可以去項目中的NHCheckTableViewProgressBar這個類。

發布:選擇熱吧,發布圖片文字

發布

發布界面相對簡單,利用masonry masonry地址 布局,處理鍵盤彈出下落通知事件,當鍵盤申彈出和下落的時候更新約束,可以看下標哥的這篇軟文 masonry約束動畫

利于UICollectionview布局圖片選擇完成后的界面,添加帶有占位文字的高度可定制化的textView。

iOS高仿App源碼:10天時間純代碼打造高仿優質《內涵段子》

iOS高仿App源碼:10天時間純代碼打造高仿優質《內涵段子》

用戶:用戶信息寫死在本地,模仿登錄邏輯

用戶

將用戶信息利用歸檔存儲在本地,用NSUserdefault記錄用戶是否在登陸狀態

修改頭像,利用彈出的自定義的ActionSheet,詳情可見NHCustomActionSheet類

項目中工具類眾多,管理類也眾多,如果您有需要或者是想了解的話可以去Github查看我的項目源碼,還有幾個比較好用的Demo也開源了。

代碼展示

@protocol NHBaseRequestReponseDelegate @required
/* 如果不用block返回數據的話,這個方法必須實現/

  • (void)requestSuccessReponse:(BOOL)success response:(id)response message:(NSString *)message; @end

typedef void(^NHAPIDicCompletion)(id response, BOOL success, NSString *message); @interface NHBaseRequest : NSObject

@property (nonatomic, weak) id nh_delegate; / 鏈接/ @property (nonatomic, copy) NSString nh_url; / 默認GET/ @property (nonatomic, assign) BOOL nh_isPost; /** 圖片數組/ @property (nonatomic, strong) NSArray *nh_imageArray;

/* 構造方法/

  • (instancetype)nh_request;
  • (instancetype)nh_requestWithUrl:(NSString *)nh_url;
  • (instancetype)nh_requestWithUrl:(NSString *)nh_url isPost:(BOOL)nh_isPost;
  • (instancetype)nh_requestWithUrl:(NSString *)nh_url isPost:(BOOL)nh_isPost delegate:(id )nh_delegate;

/* 開始請求,如果設置了代理,不需要block回調/

  • (void)nh_sendRequest; /* 開始請求,沒有設置代理,或者設置了代理,需要block回調,block回調優先級高于代理/
  • (void)nh_sendRequestWithCompletion:(NHAPIDicCompletion)completion;

@end</code></pre>

首頁最復雜的cell
@class NHBaseImageView;

typedef NS_ENUM(NSUInteger, NHHomeTableViewCellItemType) { / 點贊*/ NHHomeTableViewCellItemTypeLike = 1, // NHHomeTableViewCellItemTypeDontLike, /** 評論/ NHHomeTableViewCellItemTypeComment, /* 分享/ NHHomeTableViewCellItemTypeShare };

@class NHHomeTableViewCellFrame , NHHomeTableViewCell, NHDiscoverSearchCommonCellFrame, NHNeiHanUserInfoModel; @protocol NHHomeTableViewCellDelegate /* 分類/

  • (void)homeTableViewCellDidClickCategory:(NHHomeTableViewCell )cell; /** 個人中心/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell gotoPersonalCenterWithUserInfo:(NHNeiHanUserInfoModel )userInfoModel; /* 點擊底部item/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell didClickItemWithType:(NHHomeTableViewCellItemType)itemType; /** 點擊瀏覽大圖/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell didClickImageView:(UIImageView )imageView currentIndex:(NSInteger)currentIndex urls:(NSArray )urls; /** 播放視頻/
  • (void)homeTableViewCell:(NHHomeTableViewCell )cell didClickVideoWithVideoUrl:(NSString )videoUrl videoCover:(NHBaseImageView *)baseImageView;

@optional /* 點擊關注/

  • (void)homeTableViewCellDidClickAttention:(NHHomeTableViewCell )cell; /** 刪除/
  • (void)homeTableViewCellDidClickClose:(NHHomeTableViewCell *)cell; @end @interface NHHomeTableViewCell : NHBaseTableViewCell

/ 代理*/ @property (nonatomic, weak) id delegate; / 首頁cellFrame模型/ @property (nonatomic, strong) NHHomeTableViewCellFrame cellFrame; / 搜索cellFrame模型/ @property (nonatomic, strong) NHDiscoverSearchCommonCellFrame searchCellFrame; / 用來判斷是否有刪除按鈕*/ @property (nonatomic, assign) BOOL isFromHomeController;</code></pre>

審核,利用貝塞爾完成一些展示上的效果

  • (void)setLeftScale:(CGFloat)leftScale { _leftScale = leftScale; NSInteger leftDelta = leftScale * 100; self.leftL.text = [NSString stringWithFormat:@"%ld%%", leftDelta];

    CGFloat height = 10; UIRectCorner corner = UIRectCornerAllCorners; if (leftScale == 1.0) {

      corner = UIRectCornerAllCorners;
    

    } else {

      corner = UIRectCornerTopLeft | UIRectCornerBottomLeft;
    

    }

    UIBezierPath bezierPath0 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, self.height / 2.0 - height / 2.0, 0, height) byRoundingCorners:corner cornerRadii:CGSizeMake(5.f, 5.f)]; UIBezierPath bezierPath1 = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, self.height / 2.0 - height / 2.0, self.width * self.leftScale, height) byRoundingCorners:corner cornerRadii:CGSizeMake(5.f, 5.f)];

    CGFloat duration = 0.8; [self performSelector:@selector(showLeftAndRightLabel) withObject:nil afterDelay:duration];

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"]; animation.removedOnCompletion = NO; animation.fillMode = kCAFillModeForwards; animation.duration = duration; animation.fromValue = (bridge id _Nullable)(bezierPath0.CGPath); animation.toValue = (bridge id _Nullable)(bezierPath1.CGPath); [self.leftLayer addAnimation:animation forKey:@""]; }</code></pre>

    首頁滑動穿透效果
    // 滑動進度

  • (void)setProgress:(CGFloat)progress { _progress = progress;

    [self setNeedsDisplay]; }

  • (void)drawRect:(CGRect)rect { [super drawRect:rect]; [_fillColor set]; CGRect newRect = rect; newRect.size.width = rect.size.width * self.progress;
    UIRectFillUsingBlendMode(newRect, kCGBlendModeSourceIn); }</code></pre>

    附上自定義的一些類,項目中有自定義的ActionSheet,AlertView,SegmentControl,pageControl等,

    貼上幾段封裝的關于tableView的一些代碼
    typedef NS_ENUM(NSInteger, NHBaseTableViewRowAnimation) {
      Fade = UITableViewRowAnimationFade,
      Right = UITableViewRowAnimationRight,           // slide in from right (or out to right)
      Left = UITableViewRowAnimationLeft,
      Top = UITableViewRowAnimationTop,
      Bottom = UITableViewRowAnimationBottom,
      None = UITableViewRowAnimationNone,            // available in iOS 3.0
      Middle = UITableViewRowAnimationMiddle,          // available in iOS 3.2.  attempts to keep cell centered in the space it will/did occupy
      Automatic = 100  // available in iOS 5.0.  chooses an appropriate animation style for you
    };
    @class NHBaseTableViewCell;
    @interface NHBaseTableView : UITableView

  • (void)nh_updateWithUpdateBlock:(void(^)(NHBaseTableView *tableView ))updateBlock;
  • (UITableViewCell )nh_cellAtIndexPath:(NSIndexPath )indexPath;

/* 注冊普通的UITableViewCell/

  • (void)nh_registerCellClass:(Class)cellClass identifier:(NSString *)identifier;

/* 注冊一個從xib中加載的UITableViewCell/

  • (void)nh_registerCellNib:(Class)cellNib nibIdentifier:(NSString *)nibIdentifier;

/* 注冊一個普通的UITableViewHeaderFooterView/

  • (void)nh_registerHeaderFooterClass:(Class)headerFooterClass identifier:(NSString *)identifier;

/* 注冊一個從xib中加載的UITableViewHeaderFooterView/

  • (void)nh_registerHeaderFooterNib:(Class)headerFooterNib nibIdentifier:(NSString *)nibIdentifier;

pragma mark - 只對已經存在的cell進行刷新,沒有類似于系統的 如果行不存在,默認insert操作

/* 刷新單行、動畫默認/

  • (void)nh_reloadSingleRowAtIndexPath:(NSIndexPath *)indexPath;

/* 刷新單行、動畫默認/

  • (void)nh_reloadSingleRowAtIndexPath:(NSIndexPath *)indexPath animation:(NHBaseTableViewRowAnimation)animation;

/* 刷新多行、動畫默認/

  • (void)nh_reloadRowsAtIndexPaths:(NSArray *)indexPaths;

/* 刷新多行、動畫默認/

  • (void)nh_reloadRowsAtIndexPaths:(NSArray *)indexPaths animation:(NHBaseTableViewRowAnimation)animation;

/* 刷新某個section、動畫默認/

  • (void)nh_reloadSingleSection:(NSInteger)section;

/* 刷新某個section、動畫自定義/

  • (void)nh_reloadSingleSection:(NSInteger)section animation:(NHBaseTableViewRowAnimation)animation;

/* 刷新多個section、動畫默認/

  • (void)nh_reloadSections:(NSArray *)sections;

/* 刷新多個section、動畫自定義/

  • (void)nh_reloadSections:(NSArray *)sections animation:(NHBaseTableViewRowAnimation)animation;

pragma mark - 對cell進行刪除操作

/* 刪除單行、動畫默認/

  • (void)nh_deleteSingleRowAtIndexPath:(NSIndexPath *)indexPath;

/* 刪除單行、動畫自定義/

  • (void)nh_deleteSingleRowAtIndexPath:(NSIndexPath *)indexPath animation:(NHBaseTableViewRowAnimation)animation;

/* 刪除多行、動畫默認/

  • (void)nh_deleteRowsAtIndexPaths:(NSArray *)indexPaths;

/* 刪除多行、動畫自定義/

  • (void)nh_deleteRowsAtIndexPaths:(NSArray *)indexPaths animation:(NHBaseTableViewRowAnimation)animation;

/* 刪除某個section、動畫默認/

  • (void)nh_deleteSingleSection:(NSInteger)section;</code></pre>

    簡單易用的tableViewControllerGithub地址

    分析和總結

    • 這個項目做得時間比較倉促,前后用了不到兩周的時間。

    • 不知道仔細看的朋友有沒有意識到,這是用純代碼寫的,并不是自己不習慣用nib或者sb,是因為一直以來想用純代碼寫一個項目。

    • 所有的東西都是在公司的事情忙完的情況下編寫的,最近公司不是特別忙,所以有時間寫點自己的東西,當然下班回家晚上也花了不少時間用在了這個項目上面。

    • 項目中有些類和文件是之前自己整理的直接拖進去用,一定的意義上來說節省了時間。

    • bug有很多,我也沒怎么測直接就提交Github了,以后肯定會再更新這個項目吧

    • 下一階段的方向大概是swift項目了,現在在著手一個swift小項目,前段時間寫的,大概75%完成度了,也會在未來開源出來

     

     

    來自:http://www.cocoachina.com/ios/20160914/17576.html

     

 本文由用戶 winxp 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!