可能优化卸载在 UIScrollView 和 PageControl 中不再可见的视图控制器

发布于 2024-10-27 14:50:39 字数 2587 浏览 2 评论 0原文

我的页面控制代码与苹果示例相同。这里我有一个包含 ImageView 的子视图(controller.view)。现在问题出在内存管理上。一切正常。但是当我滚动 5-10 页时。 RAM 已满。 我尝试释放视图+控制器,但没有找到任何合适的工作位置/方式。我想发布当前不可见的视图。 (当前、上一个和下一个视图除外)

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSMutableArray *controllers = [[NSMutableArray alloc] init];
    for (unsigned i = 0; i < kNumberOfPages; i++) {
        [controllers addObject:[NSNull null]];
    }
    self.viewControllers = controllers;
    [controllers release];

    scrollView.pagingEnabled = YES;
    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.scrollsToTop = NO;
    scrollView.delegate = self;

    pageControl.numberOfPages = kNumberOfPages;
    pageControl.currentPage = 0;

    [self loadScrollViewWithPage:0];
    [self loadScrollViewWithPage:1];
}
- (void)loadScrollViewWithPage:(int)page {
    if (page < 0) return;
    if (page >= kNumberOfPages) return;

    PageControlExampleViewControl *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null]) {
        controller = [[PageControlExampleViewControl alloc] initWithPageNumber:page];
        [viewControllers replaceObjectAtIndex:page withObject:controller];
        [controller release];
    }

    if (nil == controller.view.superview) {
        CGRect frame = scrollView.frame;
        frame.origin.x = frame.size.width * page;
        frame.origin.y = 0;
        controller.view.frame = frame;
        [scrollView addSubview:controller.view];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)sender {
  if (pageControlUsed) {
        return;
    }
    CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    pageControl.currentPage = page;

    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    pageControlUsed = NO;
}
- (IBAction)changePage:(id)sender {
    int page = pageControl.currentPage;
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];
    CGRect frame = scrollView.frame;
    frame.origin.x = frame.size.width * page;
    frame.origin.y = 0;
    [scrollView scrollRectToVisible:frame animated:YES];
    pageControlUsed = YES;
}

I have this page control code same from the apple sample. Here i have a subview (controller.view) which contains a ImageView. Now problem is with memory management. All works fine. But when i scroll 5-10 pages. RAM gets filled.
I tried to release the view+controller but did not find any proper place/way that work. I want to release the views which are not currently visible. (except current,previous & next view)

- (void)applicationDidFinishLaunching:(UIApplication *)application {
    NSMutableArray *controllers = [[NSMutableArray alloc] init];
    for (unsigned i = 0; i < kNumberOfPages; i++) {
        [controllers addObject:[NSNull null]];
    }
    self.viewControllers = controllers;
    [controllers release];

    scrollView.pagingEnabled = YES;
    scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height);
    scrollView.showsHorizontalScrollIndicator = NO;
    scrollView.showsVerticalScrollIndicator = NO;
    scrollView.scrollsToTop = NO;
    scrollView.delegate = self;

    pageControl.numberOfPages = kNumberOfPages;
    pageControl.currentPage = 0;

    [self loadScrollViewWithPage:0];
    [self loadScrollViewWithPage:1];
}
- (void)loadScrollViewWithPage:(int)page {
    if (page < 0) return;
    if (page >= kNumberOfPages) return;

    PageControlExampleViewControl *controller = [viewControllers objectAtIndex:page];
    if ((NSNull *)controller == [NSNull null]) {
        controller = [[PageControlExampleViewControl alloc] initWithPageNumber:page];
        [viewControllers replaceObjectAtIndex:page withObject:controller];
        [controller release];
    }

    if (nil == controller.view.superview) {
        CGRect frame = scrollView.frame;
        frame.origin.x = frame.size.width * page;
        frame.origin.y = 0;
        controller.view.frame = frame;
        [scrollView addSubview:controller.view];
    }
}

- (void)scrollViewDidScroll:(UIScrollView *)sender {
  if (pageControlUsed) {
        return;
    }
    CGFloat pageWidth = scrollView.frame.size.width;
    int page = floor((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
    pageControl.currentPage = page;

    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];

}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    pageControlUsed = NO;
}
- (IBAction)changePage:(id)sender {
    int page = pageControl.currentPage;
    [self loadScrollViewWithPage:page - 1];
    [self loadScrollViewWithPage:page];
    [self loadScrollViewWithPage:page + 1];
    CGRect frame = scrollView.frame;
    frame.origin.x = frame.size.width * page;
    frame.origin.y = 0;
    [scrollView scrollRectToVisible:frame animated:YES];
    pageControlUsed = YES;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

暮倦 2024-11-03 14:50:39

我尝试了几个小时 Deepmist 的代码。它完成了这项工作,但我收到了内存警告,并且我的应用程序每滚动 25-30 页就会崩溃(我在页面中使用大图像)。在 Instruments 中,我注意到内存的使用量非常大:即使一次又一次地从 superview 中删除不必要的视图,并将相关的 viewController 替换为 NSNulls,Instruments 也显示,每滚动一页,实际内存就会增加 4-5MB!

在网上搜索了一下,发现这是一个常见问题。如果您也遇到此问题,您应该尝试以下检查:

1) 在每个视图中,确保使用 imageWithContentsOfFile 而不是 imageNamed。正如记录的 imageNamed 缓存图像并增加内存大小。

2) 在 Deepmist 代码中,之后:

[controller.view removeFromSuperview];

您还必须将视图设置为 nil:

controller.view=nil;

这个技巧解决了内存消耗问题,现在对于加载的唯一三个视图(当前、当前-1 和当前+1)来说内存消耗是稳定的,以避免在页面滚动)。

希望这有帮助!

I tried for several hours the code of Deepmist. It did the job but I received memory warnings and my app crashed every 25-30 pages scrolled (I am using big images in the pages). In Instruments I noticed a very large use of memory: even if the unnecessary views were removed time by time from superview and the relative viewControllers were replaced with NSNulls, Instruments showed that real memory increased on every pagescroll of 4-5MB!

Searching on the web I found that this is a common problem. If you also have this problem, you should try the following checks:

1) in each view, be sure to use imageWithContentsOfFile instead of imageNamed. As documented imageNamed cache images and increase memory size.

2) in the Deepmist code, after:

[controller.view removeFromSuperview];

you also have to set to nil the view:

controller.view=nil;

This trick solved the memory consumption which now is stable for the only three views loaded (current, current-1 and current+1 to avoid flashing in the pagescroll).

Hope this helps!

陈独秀 2024-11-03 14:50:39

我没有测试这个,但我想你可以编写一个与加载相反的方法,如下所示:

- (void)unloadScrollViewWithPage:(int)page {
    if (page < 0) return;
    if (page >= kNumberOfPages) return;

    PageControlExampleViewControl *controller = [viewControllers objectAtIndex:page];

    if ((NSNull *)controller != [NSNull null]) {
        if (nil != controller.view.superview)
            [controller.view removeFromSuperview];

        [viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];            
    }
}

然后向 didScroll 方法添加一些代码,如下所示:

[self unloadScrollViewWithPage:page - 2];
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
[self unloadScrollViewWithPage:page + 2];

I didn't test this but I imagine you could write a method that does the opposite of loading like so:

- (void)unloadScrollViewWithPage:(int)page {
    if (page < 0) return;
    if (page >= kNumberOfPages) return;

    PageControlExampleViewControl *controller = [viewControllers objectAtIndex:page];

    if ((NSNull *)controller != [NSNull null]) {
        if (nil != controller.view.superview)
            [controller.view removeFromSuperview];

        [viewControllers replaceObjectAtIndex:page withObject:[NSNull null]];            
    }
}

Then add some code to your didScroll method like so:

[self unloadScrollViewWithPage:page - 2];
[self loadScrollViewWithPage:page - 1];
[self loadScrollViewWithPage:page];
[self loadScrollViewWithPage:page + 1];
[self unloadScrollViewWithPage:page + 2];
ぃ弥猫深巷。 2024-11-03 14:50:39

在 loadScrollViewWithPage 方法中,在 if (nil ==controller.view.superview) 之前,循环遍历所有视图并删除除当前 -1、当前和当前 + 1 之外的所有视图,但前提是使用当前索引视图调用该方法。另外,不要忘记将 viewControllers 数组中的视图控制器替换为 NSNulls。

in loadScrollViewWithPage method, just before if (nil == controller.view.superview), cycle trough all views and remove them all except current -1, current and current + 1 but only if method was called with current index view. Also, don't forget to replace those view controllers in viewControllers array with NSNulls.

长安忆 2024-11-03 14:50:39

尝试 this VSScrollview ,它重用其视图,就像 UITableview 重用其单元格一样。

try this VSScrollview , it reuses its views like UITableview reuses its cell.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文