聯(lián)系方式
Head
- 這個(gè)APP是我的第一個(gè)獨(dú)立開發(fā)的作品盅藻,主要是為了方便我自己使用(當(dāng)然也方便了別人)╮(╯_╰)╭氏淑,使用的是object-c寫的假残。是一個(gè)論壇網(wǎng)站(龍的天空)的APP實(shí)現(xiàn)辉懒。有興趣的可以去上面觀摩一下。APP Store
- 這篇文章主要是介紹下本APP的一些項(xiàng)目搭建與功能實(shí)現(xiàn)
About
- 工具是用的Xcode 7.3,抓接口是用的青花瓷和chrome自帶的開發(fā)者工具快鱼。PS:其實(shí)chrome調(diào)試挺好用的- 項(xiàng)目啟動(dòng)是從4月中旬開始的线罕,然后隔三差五的花個(gè)1到2小時(shí)寫寫闻坚,中間停了一個(gè)月的時(shí)間窿凤,到7月底結(jié)束雳殊,滿打滿算的話夯秃,大概真正花了一個(gè)半月的時(shí)間吧(其實(shí)主要有一些時(shí)間花在找圖標(biāo)介陶,抓接口上了)哺呜,期間推翻界面重新設(shè)計(jì)了一次界面某残,浪費(fèi)了一些時(shí)間玻墅。
先來幾張效果圖吧
-
首頁
-
板塊
-
熱門
-
消息
-
用戶
-
帖子內(nèi)容
-
用戶交互
1.首頁
- 布局使用的
UITableView
赏酥,自定義UITableViewCell
樣式裸扶。 - 實(shí)現(xiàn)如圖所示cell距離上下左右間隔樣式呵晨,只需在自定義cell的類里重寫
setFrame
方法摸屠。如下所示:
- (void)setFrame:(CGRect)frame{
static CGFloat margin = 10;
frame.origin.x = margin;
frame.size.width -= 2 * frame.origin.x;
frame.origin.y += margin;
frame.size.height -= margin;
[super setFrame:frame];}
- 若要實(shí)現(xiàn)點(diǎn)擊cell時(shí)的背景也與cell樣式相同季二,如圖:
可以自定義cell的selectedBackgroundView
,代碼如下:
UIView *view = [[UIView alloc]initWithFrame:CGRectMake(10, 0, self.frame.size.width-20, self.frame.size.height)];
view.backgroundColor = [UIColor colorWithRed:0.96 green:0.96 blue:0.96 alpha:1.00];
view.layer.cornerRadius = 15;
view.layer.masksToBounds = YES;
self.selectedBackgroundView = view;
2.幾種cell的自定義
- 主要分這兩種桑嘶,其余的都很簡單逃顶。
- A圖主要利用了
UILable
的attributedText
屬性,同時(shí)將網(wǎng)絡(luò)請求回的帶HTML標(biāo)簽的字符串通過NSHTMLTextDocumentType
轉(zhuǎn)換即可霸褒,無需手動(dòng)截取匹配標(biāo)簽傲霸,省去了很多麻煩。代碼如下:
string = [NSString stringWithFormat:@"<head><style>img{max-width:14;max-height:14}</style></head>%@",string];
NSMutableAttributedString * messageAttrStr = [[NSMutableAttributedString alloc] initWithData:[string dataUsingEncoding:NSUnicodeStringEncoding] options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType} documentAttributes:nil error:nil]; ```
- B圖則在cell中放置了幾個(gè)
UILable
寸五,手動(dòng)截取字符串再賦值給UILable.text
即可
- cell中的時(shí)間顯示如上圖
- 將返回的時(shí)間戳
(類似于1471934323這種格式)
進(jìn)行相應(yīng)的格式化即可,代碼如下,并做好了相應(yīng)的注釋:
-(void)setTimeText:(NSInteger)dataLine{
NSString * timeString;
NSDate * todayDate = [NSDate date];//當(dāng)前時(shí)間
NSDate * pastDate = [NSDate dateWithTimeIntervalSince1970:dataLine];//發(fā)帖時(shí)間
NSDateFormatter * format = [[NSDateFormatter alloc] init];
NSCalendar * calendar = [NSCalendar currentCalendar];
NSInteger unit = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay | NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
NSDateComponents * comDate = [calendar components:unit fromDate:pastDate toDate:todayDate options:NSCalendarWrapComponents];//時(shí)間差值的組件
if (comDate.year < 1) {//判斷是否今年
if ([calendar isDateInToday:pastDate]) {//判斷是否今天
if (comDate.hour >= 1) {//判斷是否大于1小時(shí)
format.dateFormat = @"今天HH:mm";
timeString = [format stringFromDate:pastDate];
}else if(comDate.hour < 1 && comDate.minute >= 1){ //判斷是否小于1小時(shí)
timeString = [NSString stringWithFormat:@"%2ld分鐘前",(long)comDate.minute];
}else if(comDate.hour < 1 && comDate.minute < 1 && comDate.second >= 0){//判斷是否在一分鐘內(nèi)
timeString =@"剛剛";
}
}else if ([calendar isDateInYesterday:pastDate]){ //判斷是否昨天
format.dateFormat = @"昨天HH:mm";
timeString = [format stringFromDate:pastDate];
}else{ // 前天之前
format.dateFormat = @"MM-dd HH:mm";
timeString = [format stringFromDate:pastDate];
}
}else{
format.dateFormat = @"yyyy-MM-dd HH:mm";
timeString = [format stringFromDate:pastDate];
}
_time.text = timeString;
}
3.消息界面的搭建
- 主要分為上下兩個(gè)部分,上面可以點(diǎn)擊十性,若按鈕超出范圍可以滾動(dòng)劲适,下面可以滑動(dòng)切換
- 紫色部分利用
UIScrollView
來搭建霞势,紅色部分利用UICollectionView
來搭建愕贡。 - 紫色部分按鈕的文字需要在
viewWillAppear:
方法里實(shí)現(xiàn)固以,否則可能會出現(xiàn)相應(yīng)視圖未布局完成憨琳,文字不顯示栽渴。 - 紅色部分設(shè)置
UICollectionViewFlowLayout
布局與紅色大小相同闲擦,水平滾動(dòng)墅冷,間距為0即可 - 當(dāng)點(diǎn)擊按鈕時(shí)會將前一個(gè)選中狀態(tài)的按鈕取消寞忿,再給當(dāng)前按鈕添加選中狀態(tài)腔彰,然后將
UICollectionView
對象的contentOffset
給設(shè)置成按鈕對應(yīng)的tag
乘以屏幕寬度霹抛。 - 若是滑動(dòng)紅色界面杯拐,則在
UICollectionView
對象滑動(dòng)完成后的scrollViewDidEndDecelerating:
里調(diào)用設(shè)置按鈕的方法即可端逼。
4.板塊的業(yè)務(wù)邏輯實(shí)現(xiàn)
- 這個(gè)界面的UI搭建不難余掖,主要是內(nèi)部數(shù)據(jù)的請求處理比較繁瑣- 首先要請求我的已關(guān)注列表浊吏,再請求所有板塊列表找田,最后請求板塊的信息數(shù)據(jù)墩衙,網(wǎng)絡(luò)請求使用的是
AFNetworking
框架漆改。 -
AFNetworking
內(nèi)部請求是異步操作挫剑,所以需要解決請求完所有的數(shù)據(jù)再刷新的問題樊破,但所有數(shù)據(jù)請求完,需要消耗一定的時(shí)間奔滑,會給人有點(diǎn)慢的感覺朋其,因?yàn)閮?nèi)部需要做循環(huán)截取數(shù)據(jù)的操作梅猿。所以使用了數(shù)據(jù)歸檔來緩解這個(gè)問題粒没,只需要第一次請求的時(shí)候是全部請求,以后只要不清空緩存入蛆,就只需要請求關(guān)注列表和信息列表哨毁,同時(shí)扼褪,因?yàn)橛辛税鍓K列表數(shù)據(jù)话浇,即使網(wǎng)絡(luò)不好時(shí)刷新數(shù)據(jù)幔崖,也不會顯示空白赏寇,讓人等的心急嗅定。
5.搜索界面
- 分別是3個(gè)
UIView
渠退,UIView
的紫色部分是UILable
智什,綠色部分是UIButton
- 當(dāng)搜索條文字改變就會實(shí)時(shí)調(diào)用按鈕的
setTitle: forState
來改變文字荠锭,不能使用titleLabel.text
來修改证九,無效愧怜!
5.1搜索帖子列表
- 搜索的文字會標(biāo)紅拥坛,使用的是
NSMutableAttributedString
來處理字符串猜惋,將相應(yīng)的字符的位置利用Key:NSForegroundColorAttributeName
來設(shè)置顏色著摔,再將富文本整體賦值給UILable的attributedText
即可
6.發(fā)帖回帖
-
發(fā)帖
-
回帖
- 回帖是將發(fā)帖的標(biāo)題欄給隱藏了禾锤,再設(shè)置相應(yīng)的自動(dòng)約束即可
- 右上角放大縮小按鈕會根據(jù)當(dāng)前窗口的大小判斷來決定是否縮放恩掷,利用的是獲取鍵盤的高度的系統(tǒng)通知方法來控制螃成。以下為代碼范例:
#pragma mark - 當(dāng)鍵盤出現(xiàn)或改變時(shí)調(diào)用
- (void)keyboardDidShow:(NSNotification *)aNotification{
_keyHidden=NO;
NSDictionary *userInfo = [aNotification userInfo];
NSValue *aValue = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
CGRect keyboardRect = [aValue CGRectValue];
CGFloat height = keyboardRect.size.height;
CGFloat y = keyboardRect.origin.y;
_keyHeight = height;
_keyY = y;
if (self.post==nil) {
return;
}
if (self.post.message.isFirstResponder==YES||self.post.title.isFirstResponder==YES) {
if (self.post.frame.origin.y==0) {
[UIView animateWithDuration:0.1 delay:0 options:UIViewAnimationOptionLayoutSubviews animations:^{
self.post.frame=CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height-self.keyHeight-topHeight);
} completion:nil];
}else{
CGFloat maxY = self.post.frame.size.height+topHeight+self.post.frame.origin.y;
if (maxY>_keyY) {
[UIView animateWithDuration:0.1 animations:^{
CGRect ract = self.post.frame;
ract.origin.y=[UIScreen mainScreen].bounds.size.height-topHeight-(self.post.frame.size.height+_keyHeight);
self.postY = ract.origin.y;
self.post.frame = ract;
}];
}else{
self.postY = self.post.frame.origin.y;
}
}
}
}
- 發(fā)帖回帖的內(nèi)容視圖是
UITextView
,裝載的是富文本內(nèi)容氮凝。當(dāng)點(diǎn)擊圖上按鈕添加鏈接
-
可以添加鏈接:
-
然后會在發(fā)帖框內(nèi)顯示:
-
這里有個(gè)小的注意點(diǎn)竿秆,當(dāng)添加了連接后稿壁,你直接在鏈接后面輸入文字傅是,會將文字顏色和屬性更改成來鏈接文字的屬性喧笔,如圖:
- 所以需要在鏈接文字的前后各加一個(gè)空格尼变,同時(shí)設(shè)置空格的屬性為顏色黑色嫌术,字體大小等即可度气。代碼如下所示:
NSString * nullString = @" ";
urlText = [nullString stringByAppendingString:urlText];
urlText = [urlText stringByAppendingString:@" "];
NSMutableAttributedString *attributeString = [[NSMutableAttributedString alloc] initWithString:urlText];
UITextPosition * beginning = weakSelf.post.message.beginningOfDocument;
UITextRange* selectedRange = weakSelf.post.message.selectedTextRange;
UITextPosition* selectionStart = selectedRange.start;
UITextPosition* selectionEnd = selectedRange.end;
NSInteger location = [weakSelf.post.message offsetFromPosition:beginning toPosition:selectionStart];
NSInteger length = [weakSelf.post.message offsetFromPosition:selectionStart toPosition:selectionEnd];
NSRange range = NSMakeRange(location, length);[weakSelf.post.message.textStorage insertAttributedString:attributeString atIndex:range.location];
[weakSelf.post.message.textStorage addAttributes:@{NSForegroundColorAttributeName:[UIColor colorWithRed:1.00 green:0.42 blue:0.42 alpha:1.00], NSLinkAttributeName:[NSURL URLWithString:URL],NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(range.location+1, urlText.length-2)];
[weakSelf.post.message.textStorage addAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(range.location+urlText.length-1, 1)];
[weakSelf.post.message.textStorage addAttributes:@{NSForegroundColorAttributeName:[UIColor blackColor],NSFontAttributeName:[UIFont systemFontOfSize:14]} range:NSMakeRange(range.location, 1)];weakSelf.post.message.selectedRange=NSMakeRange(weakSelf.post.message.text.length,0);
- PS:這里我是在Block回調(diào)里使用的,所以使用了
__weak 類名 * weakSelf = self;
來設(shè)置weakSelf
關(guān)鍵字丙躏。
7.用戶界面
- 這里分成了3塊來布局
- 藍(lán)色部分
-
又分成3塊來布局:
-
當(dāng)紅色區(qū)塊無內(nèi)容時(shí)會隱藏
- 黃色部分為5個(gè)自定義的
UIButton
,點(diǎn)擊會跳轉(zhuǎn)到對應(yīng)的控制器 -
控制器與消息界面實(shí)現(xiàn)方式相同废恋,共用一套視圖鱼鼓,可以點(diǎn)擊按鈕或滑動(dòng)界面
-
紫色部分在無內(nèi)容時(shí)也會隱藏
8.設(shè)置界面
- 設(shè)置界面最簡單,只需要固定的幾個(gè)cell就可以嘉赎。
- 這里介紹下清除緩存的功能實(shí)現(xiàn)
- 首先是如何顯示緩存大小
- 需要獲取緩存文件夾內(nèi)容的大小,這里需要注意的是要遍歷緩存文件夾內(nèi)所有的文件來累加得到總大小拇囊,不能直接獲取文件夾大小寥袭,直接獲取到的文件夾大小與真實(shí)大小不同(當(dāng)然用戶不知道纠永,要是偷懶也可以)谒拴。代碼如下:
NSString *Path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- (NSInteger)getSizeOfPath:(NSString *)Path {
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subpaths = [mgr subpathsAtPath:Path];
NSInteger totalSize = 0;
for (NSString *subPath in subpaths) {
NSString *filePath = [directoryPath stringByAppendingPathComponent:subPath];
NSInteger fileSize = (long)[[mgr attributesOfItemAtPath:filePath error:nil] fileSize];
totalSize += fileSize;
}
return totalSize;
}
- 然后是刪除緩存炭序,只要直接將緩存文件刪除即可苍日,代碼如下:
NSString *Path = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- (void)removePath:(NSString *)Path {
NSFileManager *mgr = [NSFileManager defaultManager];
NSArray *subPaths = [mgr contentsOfDirectoryAtPath:Path error:nil];
for (NSString *subPath in subPaths) {
NSString *filePath = [directoryPath stringByAppendingPathComponent:subPath];
[mgr removeItemAtPath:filePath error:nil];
}
}
9.具體帖子內(nèi)容界面
9.1控件布局
- 上部綠框點(diǎn)擊圖所示可跳出
UIPickerView
來選擇頁面 - 下部紫框?yàn)?code>UIView,分別放置了幾個(gè)按鈕
- 功能如圖所示,一目了然拦耐,同時(shí)
View
會根據(jù)當(dāng)前界面的位置來自動(dòng)隱藏或顯示杀糯。
9.2內(nèi)容布局
- 內(nèi)容界面使用的是
WKWebview
狼纬,將HTML字符串截取合并后重新轉(zhuǎn)換成HTML格式字符串再交給WKWebview
處理骂际,同時(shí)將自定義的CSS文件一并賦值,即可控制內(nèi)容樣式没炒。 - 界面里的超鏈接或者頭像送火,都是通過截取
WKWebview
的代理方法
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler ;
里的url鏈接來進(jìn)行判斷處理种吸。
-
有圖片的界面,可以點(diǎn)擊圖片進(jìn)入圖片查看界面
- 圖片有各種手勢,縮放猖败,拖移,長按艺糜,短按等
- 這里我本來是自己實(shí)現(xiàn)的縮放破停、拖移手勢真慢,但不太完美理茎,所以就用了一個(gè)別人寫的圖片手勢的代碼修改而來
10.其他
1.點(diǎn)擊Tabbar按鈕實(shí)現(xiàn)滑動(dòng)到頂部或刷新
UITabBarController
里的UITabBarControllerDelegate
的方法
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController;```
里面來判斷當(dāng)前點(diǎn)擊的是哪個(gè)按鈕對應(yīng)的控制器朗鸠,然后根據(jù)控制器的`view`的`contentOffset`來判斷是否已經(jīng)在頂部式撼,不在則滑動(dòng)到頂部著隆,在則執(zhí)行刷新操作呀癣。
---
3.框架使用
- 網(wǎng)絡(luò)請求 [AFNetworking](https://github.com/AFNetworking/AFNetworking)
- 圖片下載 [SDWebImage](https://github.com/rs/SDWebImage)
- 數(shù)據(jù)解析 [MJExtension](https://github.com/CoderMJLee/MJExtension)
- HUD顯示 [SVProgressHUD](https://github.com/SVProgressHUD/SVProgressHUD)
- 刷 新 [MJRefresh](https://github.com/CoderMJLee/MJRefresh)
- 崩潰日志 [Bugly](https://github.com/BuglyDevTeam/Bugly-iOS)
- 然后框架導(dǎo)入使用的CocoaPods浦辨,不過最近Carthage比較流行,最近也在學(xué)習(xí)Carthage流酬。
---
4.這是我的第一個(gè)正式且上架的APP芽腾,所以也一直在更新中,主要是在iPhone手機(jī)上使用阴绢,所以在iPad上顯示時(shí)會出現(xiàn)有些布局樣式不一樣呻袭,因?yàn)闆]有進(jìn)行iPad專門的適配左电,都是交給系統(tǒng)來做的券腔。然后因?yàn)榈谝粋€(gè)嘛拘泞,所以怕不熟練,用的是最熟的OC寫的辱魁,不過接下來的幾個(gè)APP已經(jīng)轉(zhuǎn)swift了染簇,swift寫起來還是很舒服的强岸。
---
## 11.最后因?yàn)槲耶?dāng)初代碼是直接提交到國內(nèi)的Coding上去的蝌箍,還是私有庫,同時(shí)因?yàn)閼邪┰硬Γ跃蜎]有再上傳到github上弹沽,所以大家是看不到代碼了(話說代碼我寫的有些亂,也沒多少注釋炸渡。)