你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> iOS高仿App源碼:10天時間純代碼打造高仿優質《內涵段子》

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

編輯:IOS開發基礎

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

介紹:

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

939127-d3efb3e1c13b3c14.png

這個項目是用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];
}

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;

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;

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;
}

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

主要實現的功能如下:

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

首頁:

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

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

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

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

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

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


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

發現“

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

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

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

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


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

審核

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

封裝舉報底部視圖

利於UIWebView加載Gif圖,這裡的處理不是很好

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

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

發布

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

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




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

用戶

將用戶信息利用歸檔存儲在本地,用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
首頁最復雜的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;
審核,利用貝塞爾完成一些展示上的效果
- (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:@""];
}
首頁滑動穿透效果
// 滑動進度
- (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);
}

附上自定義的一些類,項目中有自定義的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;

簡單易用的tableViewControllerGithub地址

分析和總結

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

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

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

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

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

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

  • 最後,希望大家能夠提出良好的建議和見解,如果想交朋友的可以加我qq3297391688,共同進步,成為一名真正的‘老司機’,說了這麼多,你還不去給個star

    本文Github地址
    點擊前往本文Github地址



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