項目地址github:https://github.com/Eastwu5788/XMLYFM 如果您覺得不錯,記得給一個star
最新用空閒時間高仿了一下喜馬拉雅FM這款APP,目前主要完成了發現欄目中的推薦頁面。
+發現tab中有五個小分類,分別對應五個頁面,所有在“發現”的控制器中使用了UIPageViewController來控制五個子控制器。
+從Charles抓出來的接口來看,“推薦”頁面一共調用了三個接口,分別請求了推薦、熱門、直播的內容,所以在這裡選擇了Reactivecocoa來實現接口的並發訪問
- (void)refreshDataSource { @weakify(self); RACSignal *signalRecommend = [RACSignal createSignal:^RACDisposable *(idsubscriber) { @strongify(self); [self requestRecommendList:^{ [subscriber sendNext:nil]; }]; return nil; }]; RACSignal *signalHotAndGuess = [RACSignal createSignal:^RACDisposable *(id subscriber) { @strongify(self); [self requestHotAndGuessList:^{ [subscriber sendNext:nil]; }]; return nil; }]; RACSignal *signalLiving = [RACSignal createSignal:^RACDisposable *(id subscriber) { @strongify(self); [self requestLiving:^{ [subscriber sendNext:nil]; }]; return nil; }]; [[RACSignal combineLatest:@[signalRecommend,signalHotAndGuess,signalLiving]] subscribeNext:^(id x) { @strongify(self); [(RACSubject *)self.updateContentSignal sendNext:nil]; }]; } 文章轉自 耐心_朱迪的簡書
+在“推薦”頁面中有幾個輪播圖,仔細觀察會發現它的輪播圖一直想左轉換,所以這裡的輪播圖片需要做一下特殊處理。以實現無限輪播的效果
- (void)setModel:(XMLYFindFocusImagesModel *)model { _model = model; [self.adverScrollView removeAllSubViews]; self.adverScrollView.contentSize = CGSizeMake(kScreenWidth * _model.list.count, 150); //1.向scrollView中增加UIImageView的時候,需要在最後一張圖片後面將第一張圖片添加上去 for(NSInteger index = 0; index <= _model.list.count; index++) { //2.如果是最後一張圖片,則放置第一張圖片 XMLYFindFocusImageDetailModel \*detail = index == _model.list.count ? _model.list.firstObject : [_model.list objectAtIndex:index]; UIImageView \*imageView = [[UIImageView alloc] init]; imageView.frame = CGRectMake(kScreenWidth \* index, 0, kScreenWidth, 150); [imageView yy_setImageWithURL:[NSURL URLWithString:detail.pic] options:YYWebImageOptionSetImageWithFadeAnimation]; [self.adverScrollView addSubview:imageView]; } }
在輪播圖滾動動畫結束後需要做一下判斷,如果當前滾動到了最後一張圖片,則立即將scrollView的偏移調整到初始位置,這樣一個無限輪播就完成了。
- (void)scrollViewDidScroll:(UIScrollView \*)scrollView { NSInteger curPage = self.adverScrollView.contentOffset.x / kScreenWidth; if(curPage == self.model.list.count) { [self.adverScrollView setContentOffset:CGPointMake(0, 0) animated:NO]; } }
在有輪播圖的地方肯定少不了定時器,如果將定時器直接放在cell中,就會因為cell的復用導致定時器出現問題,所有一般是將定時器放在控制器中。但是這樣的話也帶來一個問題,就是由於定時器的存在,如果要求定時器的生命周期和控制器相同(也就是在控制器dealloc的時候才取消定時器).這樣的控制器是無法調用dealloc的,會造成控制器雖然已經退出但是定時器依然在正常工作。所以這裡專門為控制器設計了一個定時器的單例幫助類,這樣的話就可以在dealloc中去銷毀所有的定時器。
@interface XMLYFindRecommendHelper : NSObject #pragma mark - Common //生成幫助類單例 + (instancetype)helper; //銷毀所有的定時器 - (void)destoryAllTimer; #pragma mark - Live // 開啟為直播設置的定時器 - (void)startLiveTimer; //銷毀直播的定時器 - (void)destoryLiveTimer; #pragma mark - Header //開啟頭部的定時器 - (void)startHeadTimer; //銷毀頭部的定時器 - (void)destoryHeaderTimer; @end
在廣播頁面中,有一個根據當前時間顯示不同的問候語的小功能。比如現在是早上6點鐘,應該顯示“早安*北京”。這裡就需要用到NSDateFormatter,但是NSDateFormatter的比較消耗性能,所以我專門寫了一個XMLYTimeHelper類來管理所有的時間轉換操作。在這個類中對NSDateFormatter做了緩存處理,並使用dispatch_semaphore_t保證了線程安全。
//根據字符串生成相應的NSDateFormatter,比如"yyyy-MM-dd HH:mm:ss" static force_inline NSDateFormatter *XMLYDataCreateFormatter(NSString *string) { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"]; formatter.dateFormat = string; return formatter; } //用戶直接調用此方法,傳入"yyyy-MM-dd HH:mm:ss"這樣的字符串生成NSDateFormatter static force_inline NSDateFormatter *XMLYDateFormatter(NSString *string) { //1.檢查輸入的合法性 if(!string || ![string isKindOfClass:[NSString class]] || string.length == 0) return nil; //2.初始化單例參數 static CFMutableDictionaryRef cache; static dispatch_semaphore_t lock; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); lock = dispatch_semaphore_create(1); }); //3.加鎖 dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); //4.查詢當前字符串是否已經存在相應的NSDateformatter NSDateFormatter *formatter = CFDictionaryGetValue(cache, (__bridge const void *)(string)); //5.解鎖 dispatch_semaphore_signal(lock); //6.如果緩存中沒有,則需要重新生成 if(!formatter) { formatter = XMLYDataCreateFormatter(string); //7.重新生成成功,存入緩存 if(formatter) { dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER); CFDictionarySetValue(cache, (__bridge const void *)(string), (__bridge const void *)(formatter)); dispatch_semaphore_signal(lock); } } return formatter; }