【问题标题】:How to preload a webview with uipageviewcontroller如何使用 uipageviewcontroller 预加载 webview
【发布时间】:2018-01-10 18:40:26
【问题描述】:

我需要用uipageviewcontroller在每个页面中呈现一个webview。每个页面都是一个tableview,而webview是tableviewcell的一个属性。

现在的问题是:

翻页时会有延迟,因为加载和呈现 webview 非常耗时。所以我想通过预加载 webview 来消除这种延迟。我知道 uipageviewcontroller 将前一个和下一个 tableviewcontroller 预加载到当前的,但它只会将它们的视图加载到内存中。

因此,在调用 tableviewdatasource 方法之前,不会创建单元格,更不用说它们的属性,在我的例子中是 webview。那么如何在不改变给定视图架构的情况下预加载这些 Web 视图呢?或者有没有更好的方法,只需对给定的架构进行一点改动?

【问题讨论】:

    标签: ios


    【解决方案1】:

    UITableView 管理单元格在屏幕上呈现时的生命周期和位置。你想在这里实现的目标是 UITableView 无法适应的。

    在我看来,只有一件事要做:扔掉 UITableView 并从头开始自己的 TableView(参见下面的示例):

    @protocol TableViewDataSource <NSObject>
    
    - (NSInteger) numberOfRows;
    - (UIView *) cellForRow:(NSInteger) row;
    - (void) itemAdded;
    - (void) itemAddedAtIndex:(NSInteger)index;
    
    @end 
    
    @interface TableView : UIView <UIScrollViewDelegate>
    
    @property (nonatomic, assign) id<UIScrollViewDelegate> delegate;
    
    // the object that acts as the data source for this table
    @property (nonatomic, assign) id<TableViewDataSource> datasource;
    
    // the UIScrollView that hosts the table contents
    @property (nonatomic, assign, readonly) UIScrollView* scrollView;
    
    -(UIView*) dequeueReusableCell;
    -(NSArray*) visibleCells;
    
    // forces the table to dispose of all the cells and re-build the table.
    -(void) reloadData;
    -(void) registerClassForCells: (Class)cellClass;
    
    @end
    
    const float ROW_HEIGHT = 50.0f;
    
    @implementation TableView
    {
        UIScrollView* _scrollView;
        id<TableViewDataSource> _datasource;
    
        // a set of cells which are re-useable
        NSMutableSet* _reuseCells;
    
        // the Class which indicates the cell type
        Class _cellClass;
    }
    
    - (id)initWithCoder:(NSCoder *)aDecoder
    {
        self = [super initWithCoder:aDecoder];
        if (self) {
            _scrollView = [[UIScrollView alloc] initWithFrame:CGRectNull];
            [self addSubview:_scrollView];
    
            _scrollView.delegate = self;
            _scrollView.backgroundColor = [UIColor clearColor];
            self.backgroundColor = [UIColor clearColor];
    
            _reuseCells = [[NSMutableSet alloc] init];
        }
        return self;
    }
    
    - (void)layoutSubviews
    {
        [super layoutSubviews];
    
        _scrollView.frame = self.frame;
        [self refreshView];
    }
    
    #pragma mark - cell lifecycle
    
    -(void) refreshView
    {
        if (CGRectIsNull(_scrollView.frame))
        {
            return;
        }
    
         _scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width, [_datasource numberOfRows] * ROW_HEIGHT);
    
        for(UIView* cell in [self cellSubviews])
        {
            if (cell.frame.origin.y + cell.frame.size.height < _scrollView.contentOffset.y)
            {
                [self recycleCell:cell];
            }
    
            if (cell.frame.origin.y > _scrollView.contentOffset.y + _scrollView.frame.size.height)
            {
                [self recycleCell:cell];
            }
        }
    
        int firstVisibleIndex = MAX(0, floor(_scrollView.contentOffset.y / ROW_HEIGHT));
        int lastVisibleIndex = MIN([_datasource numberOfRows],
                                   firstVisibleIndex + 1 + ceil(_scrollView.frame.size.height / ROW_HEIGHT));
    
        for (int row =  firstVisibleIndex; row < lastVisibleIndex; row++)
        {
            UIView* cell = [self cellForRow:row];
            if (!cell)
            {
                UIView* cell = [_datasource cellForRow:row];
                float topEdgeForRow = row * ROW_HEIGHT;
                cell.frame = CGRectMake(0, topEdgeForRow, _scrollView.frame.size.width, ROW_HEIGHT);
                [_scrollView insertSubview:cell atIndex:0];
            }
        }
    }
    
    -(void) recycleCell:(UIView*)cell
    {
        [_reuseCells addObject:cell];
        [cell removeFromSuperview];
    }
    
    -(UIView*) cellForRow:(NSInteger)row
    {
        float topEdgeForRow = row * SHC_ROW_HEIGHT;
        for(UIView* cell in [self cellSubviews])
        {
            if (cell.frame.origin.y == topEdgeForRow)
            {
                return cell;
            }
        }
    
        return nil;
    }
    
    -(NSArray*)cellSubviews
    {
        NSMutableArray* cells = [[NSMutableArray alloc] init];
        for(UIView* subView in _scrollView.subviews)
        {
            if ([subView isKindOfClass:[SHCTableViewCell class]])
            {
                [cells addObject:subView];
            }
        }
        return cells;
    }
    
    #pragma mark - UIScrollViewDelegate handlers
    
    - (void)scrollViewDidScroll:(UIScrollView *)scrollView
    {
        [self refreshView];
    
        // forward the delegate method
        if ([self.delegate respondsToSelector:@selector(scrollViewDidScroll:)]){
            [self.delegate scrollViewDidScroll:scrollView];
        }
    
    }
    
    #pragma mark - UIScrollViewDelegate forwarding
    
    - (BOOL)respondsToSelector:(SEL)aSelector
    {
        if ([self.delegate respondsToSelector:aSelector]) {
            return YES;
        }
        return [super respondsToSelector:aSelector];
    }
    
    
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        if ([self.delegate respondsToSelector:aSelector]) {
            return self.delegate;
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    #pragma mark - public API methods
    
    -(void) registerClassForCells: (Class)cellClass
    {
        _cellClass = cellClass;
    }
    
    -(NSArray*) visibleCells
    {
        NSMutableArray* cells = [[NSMutableArray alloc] init];
        for(UIView* subView in [self cellSubviews])
        {
            [cells addObject:subView];
        }
        NSArray* sortedCells = [cells sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            UIView* view1 = (UIView*)obj1;
            UIView* view2 = (UIView*)obj2;
    
            float result = view2.frame.origin.y - view1.frame.origin.y;
            if (result > 0.0) {
                return NSOrderedAscending;
            } else if (result < 0.0){
                return NSOrderedDescending;
            } else {
                return NSOrderedSame;
            }
        }];
    
        return sortedCells;
    }
    
    -(UIView*)dequeueReusableCell
    {
        // first obtain a cell from the re-use pool
        UIView* cell = [_reuseCells anyObject];
        if (cell) {
            [_reuseCells removeObject:cell];
        }
    
        if (!cell) {
            cell = [[_cellClass alloc] init];
        }
        return cell;
    }
    
    -(void)reloadData
    {
        // remove all subviews
        [[self cellSubviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
        [self refreshView];
    }
    
    #pragma mark - property getters / setters
    
    - (UIScrollView *)scrollView
    {
        return _scrollView;
    }
    
    -(id<TableViewDataSource>)datasource
    {
        return _datasource;
    }
    
    -(void)setDatasource:(id<TableViewDataSource>)datasource
    {
        _datasource = datasource;
        [self refreshView];
    }
    
    @end
    

    【讨论】:

    • 太棒了,伙计!这真是一个好方法!但是我想出了另一种方法:我为tableviews预加载单元格,当调用cellForRow时,提供了预加载的单元格,所以我不需要匆忙创建单元格。但这会消耗内存。
    【解决方案2】:

    在pageviewcontroller委托方法中预加载webview

    - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController 
          viewControllerBeforeViewController:(UIViewController *)viewController;
    
    - (UIViewController *)pageViewController:(UIPageViewController *)pageViewController 
           viewControllerAfterViewController:(UIViewController *)viewController; 
    

    在 pageviewcontroller 委托方法中,您需要找到方向,如果是下一个,则在下一个 viewcontroller 旁边加载,然后将之前的 viewcontroller 释放到前一个 viewcontroller。例如您有第 1 页、第 2 页、第 3 页、第 4 页、第 5 页、第 6 页、第 7 页。假设您在横向显示两个页面,然后在 pageviewcontroller 初始化时为 page1、page2、page3 和 page4 加载 Web 视图。

    在委托方法中检查方向,如果下一步则加载第 5 页和第 6 页的 web 视图。在屏幕上显示您将拥有 page3 和 page4。当您到达第 4 页和第 5 页时,加载下 2 页并释放第 1 页和第 2 页。

    所以只需保持上一个和下一个视图控制器。释放其他视图控制器,这将帮助您解决您的问题并管理内存,因此性能也会很好。

    【讨论】:

    • 我无法在 viewDidLoad 或 viewWillAppear 中加载 webview,因为那时还没有创建单元格。所以要加载的 webview 是 nil。
    • 您是否为每个页面加载不同的视图控制器或视图。
    • 因此,当第 1 页和第 2 页可见时,我在回答加载第 3 页和第 4 页时建议加载视图。
    • 在 pageviewcontroller 委托方法中预加载 func pageViewController(UIPageViewController, didFinishAnimating: Bool, previousViewControllers: [UIViewController], transitionCompleted: Bool) 在手势驱动转换完成后调用。
    • UIPageViewController 可以帮助将下一个 VC 加载到当前的 VC 之前。我的 tableview 控制器肯定已加载,但未创建单元格。所以,我仍然无法为我的 webview 做好准备。
    猜你喜欢
    • 1970-01-01
    • 2014-08-09
    • 2013-02-02
    • 2020-09-04
    • 2021-02-01
    • 2018-07-28
    • 2017-11-05
    • 2020-08-31
    • 1970-01-01
    相关资源
    最近更新 更多