iOS小项目iOS开源&高仿项目精选技术重塑

高仿喜马拉雅FM(第一弹)

2016-09-07  本文已影响3431人  East_wu

项目地址github:https://github.com/Eastwu5788/XMLYFM 如果您觉得不错,记得给一个star
高仿喜马拉雅FM(第二弹) 本篇文章有点长了,更多的内容在第二弹继续更新

最新用空闲时间写了一下喜马拉雅FM这款APP。

干货效果演示

多音频下载、本地播放功能演示

LocalDownload.gif

网络音频播放效果演示

PlayDetail.gif

效果演示

推荐页面效果


recom.gif

分类页面效果


cate.gif

广播页面效果


radio.gif

榜单页面效果

Rank.gif

主播页面效果

Anchor.gif

订阅听页面效果

Dingyue.gif

下载听页面效果

xiazai.gif

我的页面效果

Mine.gif

分析

- (void)refreshDataSource {       

    @weakify(self);
    RACSignal *signalRecommend = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self);
        [self requestRecommendList:^{
            [subscriber sendNext:nil];
        }];
        return nil;
    }];    

    RACSignal *signalHotAndGuess = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        @strongify(self);
        [self requestHotAndGuessList:^{
            [subscriber sendNext:nil];
        }];
        return nil;
    }];
    
    RACSignal *signalLiving = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> 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];
    }
}
- (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];    
      }
}
@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

//根据字符串生成相应的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;
}

- (NSArray<UICollectionViewLayoutAttributes *> *)layoutAttributesForElementsInRect:(CGRect)rect {
    //解决issue
    NSArray* attributes = [[NSArray alloc] initWithArray:[super layoutAttributesForElementsInRect:rect] copyItems:YES];
    for(NSInteger i = 1,max = attributes.count; i < max; i++) {
        UICollectionViewLayoutAttributes *currentLayoutAttributes = attributes[i];
        UICollectionViewLayoutAttributes *prevLayoutAttributes = attributes[i - 1];
        NSInteger maximumSpacing = 0;
        NSInteger origin = CGRectGetMaxX(prevLayoutAttributes.frame);
        if(origin + maximumSpacing + currentLayoutAttributes.frame.size.width < self.collectionViewContentSize.width) {
            CGRect frame = currentLayoutAttributes.frame;
            frame.origin.x = origin + maximumSpacing;
            currentLayoutAttributes.frame = frame;
        }
    }
    return attributes;
}

注意,在获取父类的layoutAttributes数组的时候一定要选择copy,否则会报一个issue

2016-09-09 10:20:10.687 XMLYFM[1453:240776] Logging only once for UICollectionViewFlowLayout cache mismatched frame
2016-09-09 10:20:10.688 XMLYFM[1453:240776] UICollectionViewFlowLayout has cached frame mismatch for index path <NSIndexPath: 0xc000000000200116> {length = 2, path = 1 - 1} - cached value: {{106, 415}, {106.66666666666667, 162.53968253968256}}; expected value: {{106.5, 415}, {106.66666666666667, 162.53968253968256}}
2016-09-09 10:20:10.688 XMLYFM[1453:240776] This is likely occurring because the flow layout subclass XMLYAnchorFlowLayout is modifying attributes returned by UICollectionViewFlowLayout without copying them
static int64_t XMLYDiskSpaceFree() {
    NSError *error = nil;
    NSDictionary *attrs = [[NSFileManager defaultManager] attributesOfFileSystemForPath:NSHomeDirectory() error:&error];
    if (error) return -1;
    int64_t space =  [[attrs objectForKey:NSFileSystemFreeSize] longLongValue];
    if (space < 0) space = -1;
    return space;
}

1.创建子视图

- (XMLYMineHeaderView *)headerView {
    if(!_headerView) {
        //真正的头部视图
        _headerView = [[XMLYMineHeaderView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 288)];
        //设置tableHeaderView的大小与头视图相同
        self.tableView.tableHeaderView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 288)];
        //将头部视图作为UITableView子视图,遮盖掉原来的tableHeaderView
        [self.tableView addSubview:_headerView];
    }
    return _headerView;
}

2.TableView滚动时调整headerView的大小

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat offsetY = scrollView.contentOffset.y;
    if (offsetY <= 0) {
        self.headerView.frame = CGRectMake(offsetY / 2.0, offsetY, kScreenWidth - offsetY, 288 - offsetY);
    }
}

3.将headerView的frame计算放在layoutSubViews方法中,这样每一次改变headerView的frame,此方法都会走一遍,从而随之调整子视图的位置。但是千万不要把复杂位置计算放在里面

- (void)layoutSubviews { // height 288
    [super layoutSubviews];
    

    CGFloat hspace = (self.frame.size.width - kScreenWidth) / 2.0f;
    CGFloat centx = self.frame.size.width / 2.0f;
    
    //背景视图
    self.backImageView.frame = CGRectMake(hspace, 0, kScreenWidth, self.frame.size.height);
    self.alphaView.frame = CGRectMake(hspace, 0, kScreenWidth, self.frame.size.height);
    
    //节目管理
    self.managerButton.frame = CGRectMake(centx - 10 - 104.0f, self.frame.size.height - 36.0 - 37.0f, 104.0f, 37.0f);
    
    //录音按钮
    self.recordButton.frame = CGRectMake(centx + 10, self.managerButton.frame.origin.y, 104.0f, 37.0f);
    
    //子标题 
    self.subTitleLabel.frame = CGRectMake(centx - 150.0f, self.recordButton.frame.origin.y - 24.0f - 15.0f, 300, 15);
    
    //点击登录按钮
    self.userNameButton.frame = CGRectMake(centx - 100.0f, self.subTitleLabel.frame.origin.y - 10 - 18.0, 200.0f, 18.0f);
    
    self.avatarImageView.frame = CGRectMake(centx - 45.0, self.userNameButton.frame.origin.y - 10 - 90.0, 90, 90);
    
    //设置按钮
    self.settingButton.frame = CGRectMake(12 + hspace, self.avatarImageView.frame.origin.y - 20, 20, 20);
    
}

欢迎大家关注我的微信公众号

wechat.jpg
上一篇 下一篇

猜你喜欢

热点阅读