@(iOS 項目實戰(zhàn))[項目實戰(zhàn)]
- 作者: Liwx
- 郵箱: 1032282633@qq.com
目錄
- 09.項目實戰(zhàn) 百思不得姐 設(shè)置帖子圓形頭像,最熱評論處理,修復重復點擊bug,刷新控件的使用.
- 1.設(shè)置帖子頭像
- SDWebImage圖片下載失敗處理
- Xcode插件安裝路徑
- KSImageName添加自定義方法提示功能
- 2.最熱評論
- 最熱評論
- 3.拖動觸發(fā)按鈕重復點擊bug
- 4.蘋果官方刷新控件
- UIRefreshControl刷新控件
- 5.使用MJRefresh刷新框架
- MJRefresh的使用
- 補充
- AFNetworking的使用
- 使用第三方框架時子類化
- github使用
- 真機測試問題
1.設(shè)置帖子頭像
設(shè)置思路: 項目中多處用到設(shè)置圓形圖片的功能,考慮
封裝生成圓形圖片的分類UIImage+Circle
.因為頭像圖片資源都是通過url獲取,所以可以封裝設(shè)置圓形頭像分類
,將使用SDWebImage獲取圖片的操作也封裝到設(shè)置圓形頭像分類中UIImageView+Header
.
SDWebImage圖片下載失敗處理
-
圖片下載失敗處理
- 圖片下載失敗時存在問題: 如果圖片下載失敗時,image為nil,又將nil賦值給UIImageView,
會出現(xiàn)圖片下載失敗時,顯示為空白,連占位圖片都不顯示
. - 解決方案: 在
使用sd_setImageWithURL:方法加載圖
片的時候,判斷SDWebImage獲取回來的image是否為nil
,如果為nil,直接返回. - 參考代碼
// 2.加載圖片 [self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:placeholderImage completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { // 2.1 如果圖片獲取失敗,直接退出,否則會出現(xiàn)圖片加載失敗,占位圖片不顯示 if (image == nil) return; // 2.2 圖片加載成功,顯示圖片 self.image = image; }];
- 圖片下載失敗時存在問題: 如果圖片下載失敗時,image為nil,又將nil賦值給UIImageView,
-
圓形頭像處理
- 封裝
生成圓形圖片UIImage+Circle
分類- 提供生成圓形圖片的
對象方法wx_circleImage
方法. - 提供通過
圖片名
快速生成圓形圖片的類方法wx_circleImageName:
方法.
- 提供生成圓形圖片的
// ---------------------------------------------------------------------------- // 生成圓形圖片 - (instancetype)wx_circleImage { // 1.開啟圖形上下文 UIGraphicsBeginImageContextWithOptions(self.size, NO, 0); // 2.描述裁減路徑 UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; // 3.設(shè)置裁減路徑 [path addClip]; // 4.繪制圖片 [self drawAtPoint:CGPointZero]; // 5.生成新圖片 UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); // 6.關(guān)閉圖形上下文 UIGraphicsEndImageContext(); return image; } // ---------------------------------------------------------------------------- // 通過圖片名生成圓形圖片 + (instancetype)wx_circleImageName:(NSString *)imageName { return [[UIImage imageNamed:imageName] wx_circleImage]; }
- 設(shè)置圓形頭像分類
UIImageView+Header
- 設(shè)置
圓形占位圖片
. - 將使用
SDWebImage獲取圖片的操作封裝到設(shè)置圓形頭像的分類
中.
- 設(shè)置
// ---------------------------------------------------------------------------- // 設(shè)置圓形頭像 - (void)wx_setHeader:(NSString *)url { // 1.創(chuàng)建占位圖片 UIImage *placeholderImage = [UIImage wx_circleImageName:@"defaultUserIcon"]; // 2.加載圖片 [self sd_setImageWithURL:[NSURL URLWithString:url] placeholderImage:placeholderImage completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) { // 2.1 如果圖片獲取失敗,直接退出,否則會出現(xiàn)圖片加載失敗,占位圖片不顯示 if (image == nil) return; // 2.2 圖片加載成功,顯示圖片 self.image = [image wx_circleImage]; }]; }
- 封裝
Xcode插件安裝路徑
- Xcode插件安裝路徑
- 路徑一:
/Users/用戶名/Library/Application Support/Developer/Shared/Xcode/Plug-ins
- 路徑二:
/Users/用戶名/Library/Developer/Xcode/Plug-ins
- 路徑一:
KSImageName添加自定義方法提示功能
- 1.查找插件安裝路徑
獲取KSImageName在Xcode7的安裝路徑(
/Users/用戶名/Library/Developer/Xcode/Plug-ins
).-
選中KSImageNamed.ideplugin,右擊顯示包內(nèi)容,打開/Contents/Resources/Completions.plist文件,在
Completions.plist
文件中進行添加配置.- 復制插件默認的提示imageName選項,復制item0.
- 將自定義方法添加到新增的item中.
-
注意
: 修改KSImageNamed的plist配置文件,其中有個配置方法中左邊有個空格
.
Completions.plist
文件配置圖解
-
2.在KSImageName源碼中修改配置
- 從github下載KSImageName的項目,在源碼中修改配置.
3.如果
找不到資源庫
是因為本地化文件.localized
被刪除了,可以從其他文件夾中復制一份到Library中即可重新顯示資源庫
.注意:.localized
文件是隱藏文件,要確保Mac電腦已經(jīng)開啟顯示隱藏文件選項.
-
4.設(shè)置帖子頭像為圓形頭像
- 在WXTopicCell.m的模型的set方法
setTopicItem:
方法中設(shè)置圓形頭像.
// 重寫模型的set方法 - (void)setTopic:(XMGTopic *)topic { // 設(shè)置帖子圓形頭像 [self.profileImageView xmg_setHeader:topic.profile_image]; // 其他設(shè)置... }
- 在WXTopicCell.m的模型的set方法
2.最熱評論
分析: 如果服務(wù)器返回的數(shù)據(jù)有最熱評論有數(shù)據(jù)苫拍,則需要顯示最熱評論静陈,否則無需顯示最熱評論较解。最熱評論中包含語音評論围辙,需對語音評論處理嗤无。
最熱評論
-
最熱評論請求數(shù)據(jù)分析
- 請求所需參數(shù)字段(a, c, type)
- 最熱評論所需數(shù)據(jù): 用戶名(
user.username
) + 評論內(nèi)容(content
).
/**
// 服務(wù)器返回的JSON數(shù)據(jù),其中包含用戶名 + 評論內(nèi)容
{
content = "\U4e94\U767e\U5e74\U524d\U4e00\U4eba\U8e0f\U5e73\U5929\U754c\Uff0c\U4eca\U5929\U5341\U4e09\U4ebf\U4eba\U5e2e\U4f60\U8e0f\U5e73\U6625\U665a\U3002";
ctime = "2016-01-28 16:54:32";
"data_id" = 17032894;
id = 42164160;
"like_count" = 678;
precid = 0;
precmt = (
);
preuid = 0;
status = 0;
user = {
id = 16413351;
"is_vip" = 0;
"personal_page" = "http://user.qzone.qq.com/DFA2DC83F91D296282FD922FA4C45181";
"profile_image" = "http://qzapp.qlogo.cn/qzapp/100336987/DFA2DC83F91D296282FD922FA4C45181/100";
"qq_uid" = "";
"qzone_uid" = DFA2DC83F91D296282FD922FA4C45181;
sex = f;
username = "\U3001Just .";
"weibo_uid" = "";
};
voicetime = 0;
voiceuri = "";
}
*/
1.給WXTopicItem模型添加最熱評論屬性.(
NSArray *top_cmt;
)-
2.在模型的
set方法setTopicItem:
處理最熱評論(隱藏/顯示).- 最熱評論: 用戶名 + 評論內(nèi)容
- 最熱評論處理參考代碼
// ---------------------------------------------------------------------------- // 設(shè)置最熱評論 - (void)setTopCmt { // 1.獲取最熱評論數(shù)據(jù),返回數(shù)據(jù)為字典,只顯示第一條,只需取出第一條即可 NSDictionary *dict = self.topicItem.top_cmt.firstObject; // 2.判斷是否有最熱評論數(shù)據(jù) if (dict) { // 1.獲取最熱評論 用戶名 + 評論內(nèi)容 NSString *username = dict[@"user"][@"username"]; NSString *content = dict[@"content"]; // 判斷評論內(nèi)容是否為空串,空串是語音評論 if (content.length == 0) { content = @"[語音評論]"; } self.topCmtContentLabel.text = [NSString stringWithFormat:@"%@ : %@", username, content]; self.topCmtView.hidden = NO; } else { self.topCmtContentLabel.text = @""; self.topCmtView.hidden = YES; } }
-
注意: 判斷
數(shù)組有沒有內(nèi)容用count方法
,判斷字符串是不是空串,用length方法
.- 錯誤寫法
// 如果判斷數(shù)組有沒有內(nèi)容,不能使用下面的判斷 NSArray *array = @[@"abc", @"cdf"]; if (array != nil) { } if (array) { } // 如果判斷字符串是不是空串领曼,不能使用下面的判斷 NSString *content = @"abcdef"; if (content != nil) { }
3.拖動觸發(fā)按鈕重復點擊bug
-
拖動scrollView時,沒滾動到其他界面,還是在原來界面時,又會觸發(fā)按鈕重復點擊.
- bug重現(xiàn)
原因: 在監(jiān)聽scrollView停止拖動的scrollViewDidEndDecelerating:方法中調(diào)用了titleButtonClick:方法,導致只要用戶稍微左右滑動的時候就觸發(fā)標題按鈕重復點擊.
解決方案: 抽取titleButtonClick:方法在
處理標題按鈕、下劃線布持、scrollView
,封裝dealTitleButtonClick:
方法.在scrollViewDidEndDecelerating:方法中調(diào)用了dealTitleButtonClick:
方法即可.-
1.在標題按鈕監(jiān)聽的方法中,處理按鈕,下劃線滾動,scrollView滾動觸發(fā)標題按鈕重復點擊
- 重復點擊標題按鈕才發(fā)通知
- 拖動時不需要發(fā)通知
// ---------------------------------------------------------------------------- // 處理標題按鈕瑟捣、下劃線澳眷、scrollView - (void)dealTitleButtonClick:(WXTitleButton *)button { // 切換中狀態(tài) self.selectedButton.selected = NO; button.selected = YES; self.selectedButton = button; // 1.獲取索引,按鈕的tag值 NSInteger index = button.tag; // 2.執(zhí)行下劃線動畫,動畫執(zhí)行完成修改scrollView的偏移量,顯示對應(yīng)子控制器的view [UIView animateWithDuration:0.25 animations:^{ // TODO: 設(shè)置下劃線的寬度和中心點 self.underLineView.wx_width = button.titleLabel.wx_width; self.underLineView.wx_centerX = button.wx_centerX; // 切換到對應(yīng)的view self.scrollView.contentOffset = CGPointMake(self.scrollView.wx_width * index, self.scrollView.contentOffset.y); } completion:^(BOOL finished) { // 更新偏移量 CGPoint offset = self.scrollView.contentOffset; offset.x = index * self.scrollView.wx_width; [self.scrollView setContentOffset:offset]; // 添加對應(yīng)子控制器的view [self addChildVcViewIntoScrollView:index]; }]; } #pragma ======================================================================= #pragma mark - UIScrollViewDelegate代理方法 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { // 1.獲取索引 NSInteger index = self.scrollView.contentOffset.x / self.scrollView.wx_width; // 2.根據(jù)索引獲取按鈕 WXTitleButton *titleButton = self.titleView.subviews[index]; // 3.調(diào)用按鈕的點擊事件 [self dealTitleButtonClick:titleButton]; }
-
2.重復點擊標題按鈕下拉刷新
- 設(shè)置偏移量將
header偏移到titleView下面
. - 連續(xù)重復點擊時,如果
正在刷新,就不需要再刷新
. - 重復點擊標題按鈕代碼參考
// ---------------------------------------------------------------------------- // 監(jiān)聽tabBarButton重復點擊通知 - (void)tabBarButtonDidRepeatClick { // ------------------------------------------------------------------------ // 1.判斷控制器的view有沒有在window上,有沒有和window重疊 // 如果控制器的view不在window上,則直接返回 if (self.view.window == nil) return; // 如果控制器的view沒有和window重疊,則直接返回 if (![self.view wx_intersectWithView:nil]) return; // ------------------------------------------------------------------------ // 2.重復點擊,執(zhí)行下拉刷新 // 2.1 判斷當前是否在刷新,如果正在刷新直接退出 if (self.isHeaderRefreshing) return; // 2.2 更新為刷新狀態(tài) self.headerRefreshing = YES; self.headerLabel.text = @"正在刷新數(shù)據(jù)..."; self.headerLabel.backgroundColor = [UIColor greenColor]; // 2.3 設(shè)置內(nèi)邊距和偏移量,讓header處于titleView的下面 [UIView animateWithDuration:0.25 animations:^{ // 2.3.1 修改內(nèi)邊距,增加頂部內(nèi)邊距 UIEdgeInsets inset = self.tableView.contentInset; inset.top += self.header.wx_height; self.tableView.contentInset = inset; // 2.3.2 修改偏移量 CGPoint offset = self.tableView.contentOffset; offset.y = -(WXNavMaxY + WXTitlesViewH + self.header.wx_height); [self.tableView setContentOffset:offset]; }]; // ------------------------------------------------------------------------ // 3.請求數(shù)據(jù), 延遲模擬 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [self loadNewTopics]; }); }
- 設(shè)置偏移量將
- 最好考慮footer用
addSubview添加
,不用tableViewFooterView
,不要占用tableViewFooterView.
4.蘋果官方刷新控件
UIRefreshControl刷新控件
UIRefreshControl
繼承自UIControl
,可以使用addTarget方法監(jiān)聽ValueChange事件
.手動調(diào)用begin方法,不會觸發(fā)ValueChange
使用系統(tǒng)的刷新控件
UIRefreshControl控件有個bug
,在UIRefreshControl的刷新指示器正在刷新時切換到其他控制器
,再返回控制器,會出現(xiàn)刷新指示器停止動畫
了.
// TODO: 1.使用系統(tǒng)的刷新控件UIRefreshControl, UIRefreshControl控件有個問題,在UIRefreshControl的刷新指示器
// 正在刷新時切換到其他控制器,再返回控制器,會出現(xiàn)刷新指示器停止動畫了.
UIRefreshControl *header = [[UIRefreshControl alloc] init];
[header addTarget:self action:@selector(loadNewTopics) forControlEvents:UIControlEventValueChanged];
[self.tableView addSubview:header];
self.header = header;
5.使用MJRefresh刷新框架
MJRefresh的使用
-
MJRefresh的類結(jié)構(gòu)圖
- MJRefresh刷新框架中上拉刷新有2個類,下拉刷新有4個類可以使用.
- 上拉刷新: MJRefreshNormalHeader, MJRefreshGifHeader
- 下拉刷新: MJRefreshBackNormalFooter, MJRefreshBackGifFooter,MJRefreshAutoNormalFooter,MJRefreshAutoGifFooter
- MJRefresh刷新框架中上拉刷新有2個類,下拉刷新有4個類可以使用.
-
下拉scrollView時或程序進入時開始刷新,當請求完成/失敗時結(jié)束刷新.
- 開始刷新: [self.tableView.mj_header beginRefreshing];
- 結(jié)束刷新: [self.tableView.mj_footer endRefreshing];
-
統(tǒng)一設(shè)置下拉刷新顯示內(nèi)容.
- 使用繼承MJRefreshNormalHeader的自定義類WXRefreshHeader.
- 使用重寫框架的
prepare方法
或系統(tǒng)的initWithFrame方法初始化
.封裝下拉控件的屬性設(shè)置
,無需每個地方都設(shè)置. - 使用方式,只需導入自定義類WXRefreshHeader.h,設(shè)置tableView的mj_header屬性為自定義類WXRefreshHeader即可
// WXRefreshHeader.m
// ----------------------------------------------------------------------------
// 初始化子控件
- (void)prepare
{
[super prepare];
// ------------------------------------------------------------------------
// 1.設(shè)置刷新控件屬性
// 1.1 設(shè)置自動改變透明度
self.automaticallyChangeAlpha = YES;
// 1.2 設(shè)置隱藏時間
self.lastUpdatedTimeLabel.hidden = YES;
// 1.3 設(shè)置狀態(tài)文字顏色
self.stateLabel.textColor = [UIColor orangeColor];
// ------------------------------------------------------------------------
// 2.設(shè)置刷新狀態(tài)文字
[self setTitle:@"下拉刷新" forState:MJRefreshStateIdle];
[self setTitle:@"松開??上刷新" forState:MJRefreshStatePulling];
[self setTitle:@"正在玩命刷新中..." forState:MJRefreshStateRefreshing];
// ------------------------------------------------------------------------
// 添加loginImageView
UIImageView *loginImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"MainTitle"]];
[self addSubview:loginImageView];
self.loginImageView = loginImageView;
}
// ------------------------------------------------------------------------
// 使用方式, 設(shè)置為tableView的mj_header屬性,下拉刷新調(diào)用loadNewTopics方法加載網(wǎng)絡(luò)數(shù)據(jù).
self.tableView.mj_header = [WXRefreshHeader headerWithRefreshingTarget:self refreshingAction:@selector(loadNewTopics)];
// 開始刷新
[self.tableView.mj_header beginRefreshing];
-
添加下拉刷新顯示公司logo圖片
- 在
placeSubviews
或layoutSubviews
中重新布局loginImageView
// ---------------------------------------------------------------------------- // 布局子控件 - (void)placeSubviews { [super placeSubviews]; // ------------------------------------------------------------------------ // 布局loginImageView self.loginImageView.wx_centerX = self.wx_width * 0.5; self.loginImageView.wx_centerY = - self.loginImageView.wx_height; }
- 在
補充
寫UI框架考慮控件成為屬性
-
NS_REQUIRES_SUPER
: 如果在方法后面使用NS_REQUIRES_SUPER
,該方法重寫時必須調(diào)用父類的方法,如果沒有調(diào)用父類方法[super prepare]
會有警告.// 如果在方法后面使用NS_REQUIRES_SUPER,該方法重寫時必須調(diào)用父類的方法 - (void)prepare NS_REQUIRES_SUPER;
AFNetworking的使用
-
自定義繼承AFHTTPSessionManager的子類WXHTTPSessionManager
- 在子類中統(tǒng)一設(shè)置請求會話管理者的屬性
- 重寫
initWithBaseURL:sessionConfiguration:
方法,在方法中對會話管理者進行設(shè)置. - 設(shè)置請求頭,在請求頭可以添加設(shè)備類型和系統(tǒng)版本方法,參考代碼如下所示
// ---------------------------------------------------------------------------- // 初始化設(shè)置會話管理者 - (instancetype)initWithBaseURL:(NSURL *)url sessionConfiguration:(NSURLSessionConfiguration *)configuration { if (self = [super initWithBaseURL:url sessionConfiguration:configuration]) { // 設(shè)置請求頭 [self.requestSerializer setValue:@"iPhone 6s" forHTTPHeaderField:@"device"]; [self.requestSerializer setValue:@"9.2" forHTTPHeaderField:@"version"]; } return self; }
-
使用自定義類WXHTTPSessionManager執(zhí)行網(wǎng)絡(luò)請求,用法和AFHTTPSessionManager一樣.
-
查看請求頭信息
self.mgr.requestSerializer.HTTPRequestHeaders
-
使用第三方框架時子類化
使用第三方框架時如果能子類化,最好子類化封裝第三方框架,這樣如果第三方框架更新,只需更改自定義封裝的子類即可.
github使用
- Pull requests: 別人提交
- 紫色代表有整合進去
- 綠色代表有待審核
- 紅色代表沒有處理
- 點擊藍色線,查看使用的編程語言
點擊后顯示使用語言的比例
CocoaPods如果沒指定版本號,默認是最新的版本.
例如:pod 'AFNetworking'
-
第三方框架
.podspec文件
里面描述最新版本等信息.- version表示版本號.
Pod::Spec.new do |s| s.name = 'AFNetworking' s.version = '3.0.4' s.license = 'MIT' s.summary = 'A delightful iOS and OS X networking framework.' s.homepage = 'https://github.com/AFNetworking/AFNetworking' s.social_media_url = 'https://twitter.com/AFNetworking' s.authors = { 'Mattt Thompson' => 'm@mattt.me' } s.source = { :git => 'https://github.com/AFNetworking/AFNetworking.git', :tag => s.version, :submodules => true } s.requires_arc = true s.public_header_files = 'AFNetworking/AFNetworking.h' s.source_files = 'AFNetworking/AFNetworking.h'
-
github下載指定版本
- 在release中下載指定版本
真機測試問題
- 登錄界面一點程序崩潰
- 在精華控制器點擊頂部狀態(tài)欄區(qū)域,沒反應(yīng),模擬器有反應(yīng)
- 推薦標簽訂閱按鈕不好點