由于一直做公司項(xiàng)目,所以有些技能沒有去實(shí)踐過,所以一直想做一個(gè)項(xiàng)目練練手,然后找到了MONO這個(gè)軟件,里面的內(nèi)容界面都做的很精致,用charles抓了一下,發(fā)現(xiàn)接口都是可以獲取的,于是就開始做了.目前只做了一部分,記錄了過程中一些覺得值得記錄的問題,然后分享給大家.
如果大家覺得這篇文章對(duì)你有幫助,希望大家能給個(gè)star,你們的鼓勵(lì)是我前進(jìn)的動(dòng)力 --項(xiàng)目地址.
注意這里只是給出個(gè)簡單思路,詳細(xì)過程還請(qǐng)看源碼.
一.全屏拖拽效果
首先導(dǎo)入FDFullscreenPopGesture和RTRootNavigationController這兩個(gè)庫,然后在tabbar中將RTRootNavigationController設(shè)為你每個(gè)視圖控制器的根視圖就可以實(shí)現(xiàn)這種push效果了.FDFullscreenPopGesture是實(shí)現(xiàn)全屏拖拽手勢(shì),RTRootNavigationController是改變導(dǎo)航欄的動(dòng)畫效果.#
二.導(dǎo)航欄視圖填充滿
本身導(dǎo)航欄左側(cè)是始終有一個(gè)返回按鈕的位置的,如果直接設(shè)置view為titleview的話并不能填充滿,我的做法是在view上放一個(gè)backview再在backview上放控件,backview的x設(shè)為-25,這樣的話就可以將導(dǎo)航欄填充滿了.
三.簡化UITableViewDelegate,UITableViewDataSource代理方法
首先注冊(cè)cell
[self.tableView registerClass:[RecommendImageBgCell class] forCellReuseIdentifier:NSStringFromClass([RecommendImageBgCell class])];
[self.tableView registerClass:[RecommendReadCell class] forCellReuseIdentifier:NSStringFromClass([RecommendReadCell class])];
[self.tableView registerClass:[RecommendImagesCell class] forCellReuseIdentifier:NSStringFromClass([RecommendImagesCell class])];
[self.tableView registerClass:[RecommendMusicCell class] forCellReuseIdentifier:NSStringFromClass([RecommendMusicCell class])];
[self.tableView registerClass:[RecommendVideoCell class] forCellReuseIdentifier:NSStringFromClass([RecommendVideoCell class])];
[self.tableView registerClass:[RecommendPicturesCell class] forCellReuseIdentifier:NSStringFromClass([RecommendPicturesCell class])];
[self.tableView registerClass:[RecommendTeaCell class] forCellReuseIdentifier:NSStringFromClass([RecommendTeaCell class])];
高度的計(jì)算因?yàn)槲矣玫氖?a target="_blank" rel="nofollow">SDAutolayout進(jìn)行布局,所以只用一行代碼就可以將所有的cell的高度計(jì)算下來.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
RecommendModel *model = _dataArray[indexPath.row];
return [self.tableView cellHeightForIndexPath:indexPath model:model keyPath:@"recommendModel" cellClass:NSClassFromString(model.cellIdentifier) contentViewWidth:SCREEN_WIDTH];
}
這里注意所有cell的model名字需要一直,而且他們都是可以共用同樣的model.
接下來就是設(shè)置cell了,這里我給出了詳細(xì)的注釋
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
RecommendModel *model = _dataArray[indexPath.row];//獲取model
//MNBaseTableViewCell為所有cell的父類
MNBaseTableViewCell *cell;
NSString *cellIdentifier;
//后臺(tái)會(huì)根據(jù)內(nèi)容的不同給出不同的object_type,根據(jù)這個(gè)來設(shè)置不同cell的identifier.
cellIdentifier = model.cellIdentifier;
//因?yàn)榻o每個(gè)cell注冊(cè)過了,所以這里拿到identifier就可以找到具體的cell
cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
//這里用kvc給cell賦值
[cell setValue:_dataArray[indexPath.row] forKey:@"recommendModel"];
return cell;
}
四.文字閃爍出現(xiàn)
效果是下面這樣的,當(dāng)拖動(dòng)到一定位置的時(shí)候圖片上面的文字閃爍出現(xiàn),之后就不再閃爍了.
這上面的label用了第三方RQShineLabel,其實(shí)原理就是先讓所有文字字體顏色透明度為0,再加個(gè)定時(shí)器,隨機(jī)讓文字字體顏色透明度變?yōu)?.
那么如何在拖拽到一定位置的時(shí)候再執(zhí)行閃爍方法而不是剛加載這個(gè)cell的時(shí)候就執(zhí)行呢.我的做法是這樣的.
//cell將要被加載
-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
//判斷是否是需要閃爍文字的cell
if ([cell isKindOfClass:[RecommendImageBgCell class]]) {
RecommendImageBgCell *bgCell = (RecommendImageBgCell *)cell;
_bgCell = bgCell;
//如果tableview剛刷新出來,這個(gè)cell就在界面上的話就執(zhí)行閃爍方法
if (tableView.contentOffset.y <= 0 && bgCell.y <= 0) {
[_bgCell shineText];
return;
}
//給一個(gè)全局變量來監(jiān)視cell滑動(dòng)情況
_bgCellY = bgCell.y;
}
}
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
//當(dāng)cell滑動(dòng)到一定位置的時(shí)候就閃爍
if ((_bgCellY - scrollView.contentOffset.y <= (SCREEN_HEIGHT / 4 + KTabBarHeight)) && _bgCellY > 0) {
[_bgCell shineText];
_bgCellY = -10;
}
}
五.音樂播放效果
效果如下所示,當(dāng)點(diǎn)擊cell播放音樂的時(shí)候,CD會(huì)轉(zhuǎn)起來,導(dǎo)航欄最右側(cè)的CD按鈕也會(huì)轉(zhuǎn)起來.
[圖片上傳中...(13.gif-7a2593-1528170837139-0)]
首先音頻播放這塊我用的是FreeStreamer,寫一個(gè)單例類MNMusicPlayer繼承自FSAudioStream,里面除了初始化和單獨(dú)的一些方法外還為音樂播放的各種狀態(tài)添加了通知.通過通知來決定動(dòng)畫的顯示或消失.
好,接下來就在cell上自定義2個(gè)view,一個(gè)是旋轉(zhuǎn)的MusicCDView,一個(gè)是下面歌曲進(jìn)度的MusicProgressView.
在MusicCDView中,先創(chuàng)建一個(gè)CABasicAnimation,再通過給self.layer添加或移除動(dòng)畫來實(shí)現(xiàn)CD旋轉(zhuǎn)和隱藏效果.
//創(chuàng)建一個(gè)全局的動(dòng)畫
_anim = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
_anim.fromValue = [NSNumber numberWithFloat:0.f];
_anim.toValue = [NSNumber numberWithFloat: M_PI *2];
_anim.duration = 10;
_anim.autoreverses = NO;
_anim.fillMode = kCAFillModeForwards;
_anim.repeatCount = MAXFLOAT;
動(dòng)畫效果
//播放音樂
-(void)playMusic
{
if (self.alpha != 1) {
[UIView animateWithDuration:0.5 animations:^{
self.alpha = 1;
}];
}
[self.layer addAnimation:_anim forKey:@"rotaion"];
}
//暫停音樂
-(void)stopMusic
{
if (self.alpha != 0) {
[UIView animateWithDuration:0.5 animations:^{
self.alpha = 0;
}];
}
[self.layer removeAnimationForKey:@"rotaion"];
}
最后在收到通知的時(shí)候執(zhí)行就行了.
@weakify(self)
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"stopMusic" object:nil] subscribeNext:^(NSNotification *notification) {
@strongify(self)
[self stopMusic];
}];
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"playMusic" object:nil] subscribeNext:^(NSNotification *notification) {
@strongify(self)
//播放音樂的URL和此URL地址相同時(shí)才播放
if ([notification.object isEqualToString:self.model.music_url]) {
[self playMusic];
}
}];
同時(shí)別忘了給他的父視圖設(shè)置.clipsToBounds = YES屬性,不然CD盤就全部顯示出來了.
六.圖片裁剪
在圖片瀏覽中,小圖的ImageView的寬高比都是1:1的比例,而原圖的比例并不是,所以需要截取圖片正中間1:1的部分,這樣點(diǎn)擊瀏覽圖片效果就會(huì)很舒服,效果如下:
這里用到了imageView.layer.contentsRect這個(gè)屬性來截取需要顯示的圖片部分,點(diǎn)擊查看contentsRect.(這里服務(wù)器給出了原圖的寬和高了)
這里再解釋一下.
比如一張圖片如下所示,它的寬高比是2:1.
如果要截取左半部分,則這樣寫:imageView.layer.contentsRect = CGRectMake(0, 0, 0.5, 1);前2個(gè)參數(shù)表示x和y的錨點(diǎn),如果用(0,0)的話表示從最左上角開始切割,如果用(0,0.5)表示從最左邊高度的一半那里開始切割.后面2個(gè)參數(shù)表示截取的寬高比.所以這里(0, 0, 0.5, 1)后就成了這樣:
要截取正中間這部分1:1的圖片只需要設(shè)置成這樣就可以了:
imageView.layer.contentsRect = CGRectMake((1- 0.5)/2, 0, 0.5, 1);
好了,現(xiàn)在我們假設(shè)一張圖片它的寬和高分別是width和height,且width > height.那么我們要截取正中間1:1的部分就可以寫成這樣了:
imageView.layer.contentsRect = CGRectMake((1 -(float)height / width) / 2, 0, (float)height / width, 1);
同理,高圖就寫成這樣:
imageView.layer.contentsRect = CGRectMake(0, (1- (float)width / height) / 2, 1, (float)width / height);
在代碼中判斷是寬圖還是高圖再用具體的方法來截取就可以了.
七.收藏動(dòng)畫
做這個(gè)的時(shí)候走了些彎路,最后的做法是在收藏按鈕上面放兩個(gè)imageview,再設(shè)置imageview的animationImages.
八.網(wǎng)頁滑動(dòng)隱藏導(dǎo)航欄和狀態(tài)欄.
這里的做法也很簡單,如下:
-(BOOL)prefersStatusBarHidden
{
return hiddenStatusBar;
}
hiddenStatusBar是一個(gè)全局Bool變量,在滑動(dòng)的時(shí)候判斷滑動(dòng)方向后來改變hiddenStatusBar的值,再調(diào)用[self setNeedsStatusBarAppearanceUpdate]方法來刷新狀態(tài)欄的顯示或隱藏.導(dǎo)航欄的隱藏和顯示就用[self.navigationController setNavigationBarHidden:BOOL animated:YES];因?yàn)樽铋_始不知道setNeedsStatusBarAppearanceUpdate方法,所以繞了些彎路.
九.網(wǎng)頁閱讀模式
最開始看到這個(gè)閱讀模式的時(shí)候,我誤以為是safari閱讀模式,經(jīng)過仔細(xì)分析發(fā)現(xiàn)不是,他這個(gè)兩個(gè)不同的數(shù)據(jù)源而已.他們針對(duì)第三方來源的閱讀網(wǎng)頁給出來兩個(gè)數(shù)據(jù).一個(gè)是網(wǎng)址url(非閱讀模式),一個(gè)是返回html字段(閱讀模式),只需要添加2個(gè)WKWebView,轉(zhuǎn)換閱讀模式的時(shí)候只需要改變具體的WKWebView的hidden就行了.
十.加載失敗與重新加載
在視圖控制器的基類中寫兩個(gè)方法就可以,一個(gè)是加載失敗的方法.如下:
//加載失敗顯示失敗按鈕與文字,參數(shù)為點(diǎn)擊按鈕執(zhí)行方法
-(void)showPageLoadingFailedWithReloadTarget:(id)target action:(SEL)action
{
if (!pageLoadingView) {
pageLoadingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - NaviH - KTabBarHeight)];
pageLoadingView.backgroundColor = [UIColor colorWithRed:0.86 green:0.89 blue:0.91 alpha:1];
}
[pageLoadingView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.view addSubview:pageLoadingView];
UIButton *reloadImageBtn = [UIButton buttonWithType:UIButtonTypeCustom];
reloadImageBtn.frame = CGRectMake(0, 0, 40, 40);
reloadImageBtn.center = CGPointMake(pageLoadingView.width/2, pageLoadingView.height/2);
[pageLoadingView addSubview:reloadImageBtn];
[reloadImageBtn setImage:[UIImage imageNamed:@"refresh_btn"] forState:UIControlStateNormal];
UIButton *reloadBtn = [UIButton buttonWithType:UIButtonTypeCustom];
reloadBtn.frame = CGRectMake(0, reloadImageBtn.bottom + 20, 200, 30);
reloadBtn.centerX = pageLoadingView.width/2;
[pageLoadingView addSubview:reloadBtn];
reloadBtn.titleLabel.font = [UIFont systemFontOfSize:12];
[reloadBtn setTitleColor:[UIColor colorWithRed:0.66 green:0.66 blue:0.66 alpha:1] forState:UIControlStateNormal];
[reloadBtn setTitle:@"加載失敗,請(qǐng)點(diǎn)擊重試" forState:UIControlStateNormal];
if (target){
[reloadImageBtn addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
[reloadBtn addTarget:target action:action forControlEvents:UIControlEventTouchUpInside];
}
}
//顯示正在加載背景圖片
#pragma mark - 正在加載動(dòng)畫
-(void)showPageLoadingProgress
{
if (!pageLoadingView) {
pageLoadingView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT - NaviH - KTabBarHeight)];
pageLoadingView.backgroundColor = [UIColor colorWithRed:0.86 green:0.89 blue:0.91 alpha:1];
}
[pageLoadingView.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
[self.view addSubview:pageLoadingView];
UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 25, 25)];
imageView.center = CGPointMake(pageLoadingView.width/2, pageLoadingView.height/2);
[pageLoadingView addSubview:imageView];
imageView.image = [UIImage imageNamed:@"head-mask-bg"];
CAKeyframeAnimation* animation = [CAKeyframeAnimation animationWithKeyPath:@"transform"];
animation.duration = 1;// 動(dòng)畫時(shí)間
NSMutableArray *values = [NSMutableArray array];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.8, 0.8, 1.0)]];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(1.2, 1.2, 1.0)]];
[values addObject:[NSValue valueWithCATransform3D:CATransform3DMakeScale(0.8, 0.8, 1.0)]];
animation.values = values;
animation.repeatCount = FLT_MAX;
[imageView.layer addAnimation:animation forKey:nil];
}
十一.狀態(tài)欄與導(dǎo)航欄的隱藏與顯示
先說狀態(tài)欄,這里先創(chuàng)建一個(gè)BOOL值的成員變量 hiddenStatusBar,再使用方法
-(BOOL)prefersStatusBarHidden
{
return hiddenStatusBar;
}
在滑動(dòng)的時(shí)候判斷滑動(dòng)方向,改變hiddenStatusBar的值,再調(diào)用下面這個(gè)方法,這是用來調(diào)用prefersStatusBarHidden方法并刷新狀態(tài)欄狀態(tài)的
[self setNeedsStatusBarAppearanceUpdate];
導(dǎo)航欄很簡單,判斷滑動(dòng)方向然后再調(diào)用
[self.navigationController setNavigationBarHidden:NO animated:YES];
或者
[self.navigationController setNavigationBarHidden:YES animated:YES];
結(jié)尾.
這個(gè)項(xiàng)目也是費(fèi)了一些時(shí)間和精力,現(xiàn)在整理其中有遇到的或?qū)W習(xí)到的一些知識(shí)來分享給大家,希望能夠幫助到大家,目前只做了一部分,剩下的會(huì)慢慢做下去,然后再把過程中的知識(shí)點(diǎn)整理出來分享給大家.希望大家能給個(gè)star,你們的鼓勵(lì)是我前進(jìn)的動(dòng)力,謝謝大家.