自学内容网 自学内容网

知乎日报前三周总结

目录

前言

首页

网络请求

上拉加载

详情页

加载WebView

左右滑动

主页与详情页同步更新

总结


前言

在这几周进行了知乎日报的仿写,这篇博客来总结一下前三周仿写的内容

首页

首页的界面如图所示,其实就是一个导航栏和一个数据视图组成的,这个数据视图的第一个cell中存放了一个无限轮播图,UI的仿写难度并不大,主要麻烦的地方在于对网络请求接口的使用和数据的处理。 

网络请求

这里网络请求通过GCD来解决异步导致的数据未请求完成就加载UI的问题,

// 进行网络请求
    dispatch_group_enter(group);
    [manager NetWorkGetWithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {
        if (!error) {
            self.homeModel = [HomeModel yy_modelWithDictionary:userData];
            NSArray *array = self.homeModel.stories;
            for (SubModel *subModel in array) {
                [self.allArray addObject:[subModel yy_modelToJSONObject]];
            }
            NSLog(@"cishi:%d",[self.allArray count]);
        }
        dispatch_group_leave(group); // 这个需要在所有请求的最后调用
    }];

    // 确保所有请求和图片加载完成后才更新 UI
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 在这里进行视图更新
        self.homeView = [[HomeView alloc] initWithFrame:self.view.frame];
        self.homeView.tableView.delegate = self;
        self.homeView.tableView.dataSource = self;
        self.homeView.tableView.userInteractionEnabled = YES;
        [self.homeView.tableView reloadData];
        [self.view addSubview:self.homeView];
    });

这里就是把本来异步进行的网络请求,放进了一个串行队列中,当网络请求完成后,再通知视图更新UI。关于GCD的知识,笔者暂时只知道如何使用这一方法来解决异步的问题,后续学习了会再发博客进行补充。

这里申请到的文章图片只是图片的url,笔者使用了第三方库SDWebImage来加载图片,一开始我的思路是先将数据申请和图片加载都完成,并将数据和图片保存在数组中,加载UI时直接访问数组中对应元素,但是这样就会出现异步导致数组为空的问题,因为需要同时进行两个后台请求,当时无法解决,后来意识到在最外层的请求完成后再退出队列就好了。当时笔者改变了思路,将申请到的model传给view层,当view层需要访问图片时,再获取数组中的url并调用方法加载,这样既不会有问题了。

上拉加载

在首页还有一个比较重要的功能,也是笔者最新学到的,就是上拉加载的写法。

实现tableView中的下拉上拉刷新效果及相关基础概念

参考这篇文章,笔者在首页中实现了上拉加载的效果,简单来说,就是在tableView的下方创建一个视图,当tableView偏移量大于某一个值时,就调用方法获取新的数据,再将新的数据更新到UI上,并更新tableView下方的视图。主要这里获取新的数据时需要更新url,笔者使用了NSDate来处理日期。

- (void)reloadDate {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyyMMdd"];
    NSString* dateString = [formatter stringFromDate:self.date];
    NSLog(@"%@", dateString);
    dispatch_group_t group = dispatch_group_create();
    Manager *manager = [Manager shareManeger];
    // 进行网络请求
    dispatch_group_enter(group);
    [manager NetWorkGetBefore: dateString WithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {
        if (!error) {
            BeforeModel* beforeModel = [[BeforeModel alloc] init];
            beforeModel = [BeforeModel yy_modelWithDictionary:userData];
            [self.beforeArray addObject:beforeModel];
            NSArray *array = beforeModel.stories;
            for (BeforeSubModel *beforeSubModel in array) {
                [self.allArray addObject:[beforeSubModel yy_modelToJSONObject]];
            }
            NSLog(@"%@",self.allArray);
        }
        dispatch_group_leave(group); // 这个需要在所有请求的最后调用
    }];

    // 确保所有请求和图片加载完成后才更新 UI
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 在这里进行视图更新
        [self.homeView.tableView reloadData];
        self.homeView.footer.frame = CGRectMake(0, self.homeView.tableView.contentSize.height + 300, 394, 50);
        [self.homeView.footerLabel setText:@"下拉可以刷新"];
        self.homeView.footerRefreshing = NO;
        
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDateComponents *oneDayBeforeComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:self.date];
        oneDayBeforeComponents.day -= 1;
        if (oneDayBeforeComponents.day < 1) {
            NSRange dayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];
            oneDayBeforeComponents.day = dayRange.length;
            oneDayBeforeComponents.month -= 1;
            if (oneDayBeforeComponents.month < 1) {
                oneDayBeforeComponents.month = 12;
                oneDayBeforeComponents.year -= 1;
                NSRange newDayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];
                oneDayBeforeComponents.day = newDayRange.length;
            }
        }
        self.date = [calendar dateFromComponents:oneDayBeforeComponents];
    });
}
- (void)dealFooter {
    if (self.homeView.footerRefreshing) {
        return;
    }
    CGFloat footerOffset = self.homeView.tableView.contentSize.height - self.homeView.tableView.frame.size.height + self.homeView.footerLabel.frame.size.height + 100;
//    NSLog(@"%d %d %d", self.homeView.tableView.contentSize.height, self.homeView.tableView.frame.size.height, self.homeView.footerLabel.frame.size.height);
    if (self.homeView.tableView.contentOffset.y >= footerOffset) {
        [self footerBeginRefreshing];
    }
}

- (void)footerBeginRefreshing {
    if (self.homeView.footerRefreshing) {
        return;
    }
    [self.homeView.footerLabel setText:@"正在刷新数据"];
    self.homeView.footerRefreshing = YES;
//    [UIView animateWithDuration:0.25 animations:^{
//        UIEdgeInsets inset = self.homeView.tableView.contentInset;
//        inset.top += self.homeView.footerLabel.bounds.size.height;
//        self.homeView.tableView.contentInset = inset;
//    }];
    [self reloadDate];
}

详情页

加载WebView

详情页这里,webView的url可以通过接口获取到,需要调用方法将webView加载出来,笔者这里在使用webView前,加入了"WKWebView+AFNetworking.h"这个头文件。

NSURL *urlWeb = [NSURL URLWithString:self.allArray[self.page - 1][@"url"]];
NSURLRequest *webRequest = [[NSURLRequest alloc] initWithURL:urlWeb];
[self.wkWebView loadRequest:webRequest];
[self.view addSubview: self.wkWebView];

这样就可以加载出webView

左右滑动

详情页的一个比较重要的功能就是要实现左右滑动,这里也是笔者认为详情页面最复杂的地方。笔者的思路是计算每一个cell的位置,当点击某个cell时,将位置往controller和view层传值,并根据cell的位置来决定webView的位置和拿来加载webView的url在数组中的位置。每当滑动视图滑动到每一天的最后一页时,就加载后一天的数据,每滑动一页,就加载当前页的视图。

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    
    if (scrollView.contentOffset.x <= 0) {
            // 滚动到最左端,禁止继续向左滑动
            scrollView.contentOffset = CGPointMake(0, 0);
        }
    
    NSInteger currentPage = (scrollView.contentOffset.x / [UIScreen mainScreen].bounds.size.width) + 1;
    self.newpage= [NSNumber numberWithInteger:currentPage + 5];

        // 当滚动视图向右滚动且快接近画布右边缘时,触发加载数据的操作
    if (scrollView.contentOffset.x >  ([self.allArray count] * [UIScreen mainScreen].bounds.size.width  - [UIScreen mainScreen].bounds.size.width * 1.5) && self.isLoadingMoreData == NO) {
        self.isLoadingMoreData = YES;
        [self loadMoreData:(NSInteger)currentPage + 1];
    }
    if (currentPage != (self.webview.page) && isLoadingWebView == NO && self.isLoadingMoreData == NO && ![self.pageSet containsObject:self.newpage]) {
        NSLog(@"yin:%d %d %d",self.webview.page ,currentPage, [self.allArray count]);
        isLoadingWebView = YES;
        self.webview.page = currentPage;
        [self.pageSet addObject:self.newpage];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"newPage" object:nil userInfo:nil];
    }
}

除此之外,还要实时更新下方工具栏中的点赞和评论数量,笔者这里不保存点赞和评论的数量,每一次滑动到一个新的视图,就重新加载一次点赞数和评论数。

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSInteger currentPage = (scrollView.contentOffset.x / [UIScreen mainScreen].bounds.size.width) + 1;
    if(isLoadingWebView == NO && _isLoadingMoreData == NO) {
        dispatch_group_t group = dispatch_group_create();
        Manager *manager = [Manager shareManeger];
        
        // 进行网络请求
        dispatch_group_enter(group);
        [manager NetWorkGetFor:self.allArray[currentPage][@"id"]WithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {
            if (!error) {
                ExtraModel* extraModel = [ExtraModel yy_modelWithDictionary:userData];
                self.webview.likeLabel.text = [NSString stringWithFormat:@"%d",extraModel.popularity];
                self.webview.commentLabel.text = [NSString stringWithFormat:@"%d",extraModel.comments];
            }
            dispatch_group_leave(group);
        }];
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        });
    }
}

主页与详情页同步更新

这里笔者通过两个数组来实现同步更新,一个存放所有文章数据的字典,一个存放每天数据的字典,在从主页点击详情页时,直接通过属性传值,而在详情页加载好数据后,就只能通知传值并调用方法更新tableView和上拉加载的底部视图就好

- (void)loadMoreData:(NSInteger)currentPage {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyyMMdd"];
    NSString* dateString = [formatter stringFromDate:self.date];
    NSLog(@"%@", dateString);
    NSMutableDictionary *updateDictionary = [[NSMutableDictionary alloc] init];
    dispatch_group_t group = dispatch_group_create();
    Manager *manager = [Manager shareManeger];
    // 进行网络请求
    dispatch_group_enter(group);
    [manager NetWorkGetBefore: dateString WithCompletion:^(NSDictionary * _Nonnull userData, NSError * _Nonnull error) {
        if (!error) {
            BeforeModel* beforeModel = [[BeforeModel alloc] init];
            beforeModel = [BeforeModel yy_modelWithDictionary:userData];
            NSArray *array = beforeModel.stories;
            for (BeforeSubModel *beforeSubModel in array) {
                [self.allArray addObject:[beforeSubModel yy_modelToJSONObject]];
            }
            [updateDictionary setValue:beforeModel forKey:@"beforeModel"];
            [updateDictionary setValue:self.allArray forKey:@"allArray"];
        }
        dispatch_group_leave(group); // 这个需要在所有请求的最后调用
    }];

    // 确保所有请求和图片加载完成后才更新 UI
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 在这里进行视图更新
        
        NSCalendar *calendar = [NSCalendar currentCalendar];
        NSDateComponents *oneDayBeforeComponents = [calendar components:NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:self.date];
        oneDayBeforeComponents.day -= 1;
        if (oneDayBeforeComponents.day < 1) {
            NSRange dayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];
            oneDayBeforeComponents.day = dayRange.length;
            oneDayBeforeComponents.month -= 1;
            if (oneDayBeforeComponents.month < 1) {
                oneDayBeforeComponents.month = 12;
                oneDayBeforeComponents.year -= 1;
                NSRange newDayRange = [calendar rangeOfUnit:NSCalendarUnitDay inUnit:NSCalendarUnitMonth forDate:self.date];
                oneDayBeforeComponents.day = newDayRange.length;
            }
        }
        self.date = [calendar dateFromComponents:oneDayBeforeComponents];
        [updateDictionary setValue:self.date forKey:@"date"];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"update" object:nil userInfo:updateDictionary];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"layoutNewScrollView" object:nil userInfo:nil];
        self.isLoadingMoreData = NO; // 重置加载标志
        if (currentPage != (self.webview.page) && isLoadingWebView == NO && self.isLoadingMoreData == NO && ![self.pageSet containsObject:self.newpage]) {
            isLoadingWebView = YES;
            self.webview.page = currentPage;
            [self.pageSet addObject:self.newpage];
            [[NSNotificationCenter defaultCenter] postNotificationName:@"newPage" object:nil userInfo:nil];
        }
        
    });
}

这里笔者通知传值用的字典dictionary初始化和赋值是在不同的区域进行的,因为这个dictionary中不仅要保存新申请到的数据,还要保存更新后的日期,所以初始化必须在让dictio全区可以访问的位置, 赋值一个在获取完数据之后,另一个在日期更新之后。

总结

后续的评论区和收藏中心两个界面,笔者已经完成了评论区的数据请求,后续学习UItextView来对界面布局,收藏中心需要调用第三方库,笔者也将在后续完成


原文地址:https://blog.csdn.net/2301_80095702/article/details/143659777

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!