UIScrollView用法_uiscrollview updateframe-程序员宅基地

技术标签: Iphone开发心得  api  uiview  alignment  框架  iphone  图形  

        UIScrollView是iphone中的一个重要的视图,它提供了一个方法,让你在一个界面中看到所有的内容,从而不必担心因为屏幕的大小有限,必须翻到下一页进行阅览。确实对于用户来说是一个很好的体验。但是又是如何把所有的内容都加入到scrollview,是简单的addsubView。假如是这样,岂不是scrollView界面上要放置很多的图形,图片。移动设备的显示设备肯定不如PC,怎么可能放得下如此多的视图。所以在使用scrollView中一定要考虑这个问题,当某些视图滚动出可见范围的时候,应该怎么处理,是不管它那,还是进行内存回收或者重利用。苹果公司的UITableView就很好的展示了在UIScrollView中如何重用可视的空间,减少内存的开销。

    首先还是看官方API如何解释UIScrollView,一下是翻译官方UIScrollView的帮助文档。

    UIScrollView类支持显示比屏幕更大的应用窗口的内容。它通过挥动手势,能够使用户滚动内容,并且通过捏合手势缩放部分内容。

    UIScrollView是UITableView和UITextView的超类。

    UIScrollView的核心理念是,它是一个可以在内容视图之上,调整自己原点位置的视图。它根据自身框架的大小,剪切视图中的内容,通常框架是和应用程序窗口一样大。一个滚动的视图可以根据手指的移动,调整原点的位置。展示内容的视图,根据滚动视图的原点位置,开始绘制视图的内容,这个原点位置就是滚动视图的偏移量。ScrollView本身不能绘制,除非显示水平和竖直的指示器。滚动视图必须知道内容视图的大小,以便于知道什么时候停止;一般而言,当滚动出内容的边界时,它就返回了。

    某些对象是用来管理内容显示如何绘制的,这些对象应该是管理如何平铺显示内容的子视图,以便于没有子视图可以超过屏幕的尺寸。就是当用户滚动时,这些对象应该恰当的增加或者移除子视图。

    因为滚动视图没有滚动条,它必须知道一个触摸信号是打算滚动还是打算跟踪里面的子视图。为了达到这个目的,它临时中断了一个touch-down的事件,通过建立一个定时器,在定时器开始行动之前,看是否触摸的手指做了任何的移动。假如定时器行动时,没有任何的大的位置改变,滚动视图就发送一个跟踪事件给触摸的子视图。如果在定时器消失前,用户拖动他们的手指足够的远,滚动视图取消子视图的任何跟踪事件,滚动它自己。子类可以重载touchesShouldBegin:withEvent:inContentView:,pagingEnabled,和touchesShouldCancelInContentView:方法,从而影响滚动视图的滚动手势。

    一个滚动视图也可以控制一个视图的缩放和平铺。当用户做捏合手势时,滚动视图调整偏移量和视图的比例。当手势结束的时候,管理视图内容显示的对象,就应该恰当的升级子视图的显示。当手势在处理的过程中,滚动视图不能够给子视图,发送任何跟踪的调用。

    UIScrollView类有一个delegate,需要适配的协议是UIScrollViewDelegate。为了缩放和平铺工作,代理必须实现viewForZoomingInScrollView:和scrollViewDidEndZooming:withView:atScale:方法。另外,最大和最小缩放比例应该是不同的。

    重要的提示:在UIScrollView对象中,你不应该嵌入任何UIWebView和UITableView。假如这样做,会出现一些异常情况,因为2个对象的触摸事件可能被混合,从而错误的处理。

    这些都是官方API的解释,重点是理解UIScrollView怎么来控制手势的。可以由canCancelContentTouches这个方法的运用来解释UIScrollView如何控制手势的。

    假如你设置canCancelContentTouches为YES,那么当你在UIScrollView上面放置任何子视图的时候,当你在子视图上移动手指的时候,UIScrollView会给子视图发送touchCancel的消息。而如果该属性设置为NO,ScrollView本身不处理这个消息,全部交给子视图处理。

    那么这里就有疑问了,既然该属性设置未来NO了,那么岂不是UIScrollView不能处理任何事件了,那么为何在子视图上快速滚动的时候,UIScrollView还能移动那。这个一定要区分前面所说的UIScrollView中断touch-Down事件,开启一个定时器。我们设置的这个cancancelContentTouches属性为NO时,只是让UIScrollView不能发送cancel事件给子视图。而前面所说的时,中断touch-down事件,和取消touch事件是俩码事,所以当快速在子视图上移动的时候,当然可以滚动。但是如果你慢速的移动的话,就可以区分这个属性了,假如设定为YES,在子视图上慢速移动也可以滚动视图,但是如果为NO 。因为UIScrollView,发送了cancel事件给子视图处理了,自己当然滚动不了了。

    事件处理看过了,就要考虑scrollView如何重用内存的,下面写了一个例子模仿UITableView的重用的思想,这里只是模仿,至于苹果公司怎么实现这种重用的,他们应该有更好的方法。

     这里的例子是在scrollView上放置4个2排2列的视图,但是内存中只占用6个视图的内存空间。当scrollView滚动的时候,通过不停的重用之前视图的内存空间,从而达到节省内存的效果。重用的方法如下:

1.如果scrollView向下面滚动,一旦一排视图滚出了可视范围,就改变滚动出去的那个view在scrollView中的frame,也就是改变位置到达末尾,达到重用的效果。

2.如果scrollView向上面滚动,一旦最末排的视图view滚出了可视范围,就改变滚动出去的那个view在scrollView中的frame,移动到最前面。

   下面就需要在你创建的视图控制器中,创建一个重用的视图数组,用来把这些要显示的视图放入内存中,这里虽然界面上显示的是2排2列的四个视图,但是当拖动的时候,可能出现前面一排的视图显示一部分,末尾一排的视图显示一部分的情况,所以重用的数组中要放置6个视图。下面是定义的一些宏:

#define sMyViewTotal 6

#define sMyViewWidth 150

#define sMyViewHeight 220

#define sMyViewGap 10


具体实现代码如下:

    _aryViews = [[NSMutableArray alloc] init];

    for (int i = 0; i < sMyViewTotal; i++) {

        CGFloat x;

        if (i%2) {

            x = sMyViewWidth + sMyViewGap + sMyViewGap/2;

        }

        else{

            x = sMyViewGap/2;

        }

        CGFloat y = (sMyViewHeight + sMyViewGap)*(i/2);

        MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(x, y, sMyViewWidth, sMyViewHeight)];

        myView.showNumber = i;

    [myScrollView addSubview:myView];

      [_aryViews addObject:myView];

        [myView release];

    }


所以这里的核心方法是,首先要判断是向上滚动还是向下滚动方法如下:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView{

    BOOL directDown;

    if (previousOffSet.y < scrollView.contentOffset.y) {

        directDown = YES;

    }

    else{

        directDown = NO;

    }

    previousOffSet.y = scrollView.contentOffset.y;

    //防止最开始就向上面拖动的时候,改变数组视图树的位置。

    if (scrollView.contentOffset.y < 0) {

        return;

    }

    if (directDown) {

        NSLog(@"down");

        MyView * subView = [_aryViews objectAtIndex:firstViewIndex];

        CGFloat firstViewYOffset = subView.frame.origin.y + subView.frame.size.height + sMyViewGap;

        //寻找第一个视图是否滚动出去

        if (firstViewYOffset < scrollView.contentOffset.y) {

            //改变数组中第一排可见视图的位置。

            [self moveIndexInViewsWithDirect:YES];

        }

    }

    else{

        NSLog(@"up");

        MyView * subView = [_aryViews objectAtIndex:(firstViewIndex + sMyViewTotal - 2)%sMyViewTotal];

        CGFloat lastViewYOffset = subView.frame.origin.y - scrollView.bounds.size.height;

        if (lastViewYOffset > scrollView.contentOffset.y) {

                           [self moveIndexInViewsWithDirect:NO];

        }

    }

}


每次滚动的时候先判断滚动位置即offset,和先前的比较。如果先前的大就是向下滚动,否则就是向上滚动。

    找到了向下滚动了,就该判断是否子视图已经离开了可视范围。方法就是判断当前offset和视图的位置进行比较。如果判断滚到离开了可视范围,然后就是要改变重用视图数组中第一个视图的位置了。这里用了firstViewIndex来记录scrollView中第一个可见视图的位置, 循环使用这6个视图达到重用的目的。自然firstViewIndex上面的一个视图就是最后一个视图的位置(firstViewIndex + sMyViewTotal - 1)%sMyViewTotal。所以这里需要改变重用视图中firstViewIndex即第一个可见视图的位置。代码如下:

- (void)moveIndexInViewsWithDirect:(BOOL)forward{

    [UIView setAnimationsEnabled:NO];

    if (forward) {

        for (int i = firstViewIndex; i < (firstViewIndex + 2); i++) {

            MyView *subView = [_aryViews objectAtIndex:i%sMyViewTotal];

            subView.showNumber = subView.showNumber + sMyViewTotal;

subView.frame = CGRectMake(subView.frame.origin.x, subView.frame.origin.y + (sMyViewTotal/2) * (sMyViewHeight + sMyViewGap), subView.frame.size.width, subView.frame.size.height);

        }

        firstViewIndex = (firstViewIndex + 2)%sMyViewTotal;

    }

    else{

        int lastViewIndex = firstViewIndex + sMyViewTotal - 1;

        for (int i = lastViewIndex; i > (lastViewIndex - 2); i--) {            MyView *subView = [_aryViews objectAtIndex:(firstViewIndex + sMyViewTotal - i)%sMyViewTotal];

            subView.showNumber = subView.showNumber - sMyViewTotal;

subView.frame = CGRectMake(subView.frame.origin.x, subView.frame.origin.y - (sMyViewTotal/2) * (sMyViewHeight + sMyViewGap), subView.frame.size.width, subView.frame.size.height);

        }

        firstViewIndex = (firstViewIndex + sMyViewTotal - 2)%sMyViewTotal;

    }

    [UIView setAnimationsEnabled:YES];

}


  这里创建的子视图数字属性,是用来在视图上画数字的,这样就可以看到视图重用的效果了,应该是从0开始到无穷多,但是实际上内存中就创建了6个视图。 

- (void)drawRect:(CGRect)rect

{

    // Drawing code

    NSString *text = [NSString stringWithFormat:@"%d",showNumber];

    [[UIColor redColor] set];

    [text drawInRect:CGRectMake(rect.origin.x, rect.origin.y + rect.size.height/2 - 30, rect.size.width, 30) withFont:[UIFont      fontWithName:@"Helvetica" size:20] lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentCenter];

}

}

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mengtnt/article/details/6723245

智能推荐

TypeScript报错信息表(备注)_an index signature must have a type annotation.-程序员宅基地

文章浏览阅读1.3w次,点赞2次,收藏5次。code 类型 英文描述 中文描述1002 错误 Unterminated string literal. 未终止的字符串文本。1003 错误 Identifier expected. 应为标识符。1005 错误 '{0}' expected. 应为“{0}”。1006 错误 A file cannot have a reference to itself. 文件不能引用自身。1009..._an index signature must have a type annotation.

安卓onOverScrolled方法与onScrollChanged方法的详解与区别-程序员宅基地

文章浏览阅读2.1w次,点赞5次,收藏6次。今天在使用Scroview的时候注意到一个方法onOverScrolled ,百度了半天感觉介绍都不是很详细故查找Scroview的API终于了解了他的原理 在使用此方法的时候实现效果跟onScrollChanged方法实现效果类似但是参数不相同@Overrideprotected void onScrollChanged(int l, int t, int oldl, int old_onoverscrolled

Centos7安装Redis7.x最新稳定版|配置开机启动(骨灰级|保姆级)_tar xzf redis-7.2.4.tar.gz 没有 configure-程序员宅基地

文章浏览阅读830次,点赞3次,收藏3次。Centos7安装最新稳定版Redis7Redis7配置开机启动Centos7 Redis开机启动脚本编写_tar xzf redis-7.2.4.tar.gz 没有 configure

C语言disc函数,【职场之道】DISC行为模式深入了解-程序员宅基地

文章浏览阅读2.2k次。来源:网络编辑:磚頭哥上周一咱们从西游记里,聊了DISC行为模式的基本特性,今天磚頭哥再跟着大家,深入的讨论下DISC行为模式,以及如何利用DISC来使自己更好的工作,学习。1D型(支配/老板型)看过上周一文章的各位都应该知道,脾气大有魄力,高支配力的D型,我们也称之为老虎型。表现在平时的特点为:主动与他人握手,而且很用力从来不怕目光直视对方,而且表情严厉,让人望而生畏说话的口吻常常是命令式的在谈..._disc在c语言中的作用

DZ论坛 数据库 表 字段 详解 (转载)-程序员宅基地

文章浏览阅读841次。DZ论坛 数据库 表 字段 详解 (转载) DZ论坛 数据库 表 字段 详解 (转载) dz表结构详解cdb_access 用户权限表 cdb_adminactions 管理动作表cdb_admingroups 管理组数据表cdb_adminnotes ..._dz reasonpm

linux搭建nginx+rtmp+nginx-http-flv-module环境_nginx-http-flv-module-master-程序员宅基地

文章浏览阅读3.7k次。一、rtmp局限性 由于各家浏览器到今年年底不在支持flash,所以在视频监控将rtsp流转换成让rtmp的方式需要改进。二、环境准备 1、下载nginx包 下载地址:https://nginx.org/download/nginx-1.14.2.tar.gz 2、下载nginx-http-flv-module 模块包 下载地址:下载地址:https://github.com/winshining/nginx-http-flv-m..._nginx-http-flv-module-master

随便推点

OMEN惠普HP暗夜精灵5:win10下安装Ubuntu16.04双系统(win10+linux)_hp win ubuntukylin 双系统-程序员宅基地

文章浏览阅读3.1k次,点赞7次,收藏16次。笔记本电脑为:OMEN5 15.6英寸 i5-9300H 24G 512SSD+1T GTX1660Ti(自己加装了内存条和机械硬盘)一、准备工作1.下载并安装软碟通:百度搜索免费下载UltraISO软碟通官方中文版2.安装,使用软碟通3.ubuntu官网下载ios镜像文件,我们选择的是桌面镜像版:网址:http://releases.ubuntu.com/16.04/选择64位桌面版进行下载4.先分区,选择至少80G的空间从一个较大的卷中:“压缩卷”,不要设置卷5.打开_hp win ubuntukylin 双系统

基于SSM的珠宝店信息管理系统--87229(免费领源码)可做计算机毕业设计JAVA、PHP、爬虫、APP、小程序、C#、C++、python、数据可视化、大数据、全套文案-程序员宅基地

文章浏览阅读948次,点赞25次,收藏19次。本系统在详细的需求分析的基础上,系统设计使用SSM框架,采用基于MVVM模式进行开发,使用Eclipse为编写工具,数据方面主要采用的是微软的MySQL关系型数据库来作为数据存储媒介等完成系统的开发,完成了系统的主要模块的页面设计和功能实现。

WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connec-程序员宅基地

文章浏览阅读3.8k次,点赞13次,收藏5次。WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'SSLError(SSLEOFError(8, 'EOF occurred in violation of protocol (_ssl.c:852)'),)'解决方法:关掉代理软件……_warning: retrying (retry(total=0, connect=none, read=none, redirect=none, st

《C#图解教程》读书笔记1-2章——C#和.NET_c#图解教程 读书笔记-程序员宅基地

文章浏览阅读1.7k次。 这本书讲解C#语言十分详细,我将其中的重要内容整理成条款,以备忘。1. .NET由三部分组成:CLR,编码工具和BCL。CLR负责内存管理、代码安全验证、代码执行、垃圾收集。.NET提供平台调用(P/Invoke),允许调用Windows API,而且还可以和COM进行交互操作_c#图解教程 读书笔记

操作系统中的同步和异步_操作系统异步-程序员宅基地

文章浏览阅读2.5w次,点赞70次,收藏177次。操作系统中同步、异步性概念首先我们从操作系统的发展中学习什么是异步性。在操作系统发展的初期阶段,CPU处理的是作业,而且是单道批处理。什么意思呢?就是一个作业从提交到结束,程序员都不能干预,此时整台计算机就为这一个作业服务(可想有多少资源被"浪费"),这样有一点好处就是整个程序是"封闭的"。这样的操作表明人和机器是没有交互的。那我们怎么实现人机交互呢?这个答案是中断。中断的引入,使得工作人员能..._操作系统异步

arm-linux-gnueabi-gcc 和 arm-linux-gnueabihf-gcc的区别 ——ARM交叉编译器的学习(学习记录)_gcc 和 arm-linux-guneabihf-程序员宅基地

文章浏览阅读4.3k次,点赞10次,收藏16次。**一:详聊**一起对于Linux的很多命令,自己都是傻瓜式的照敲的,现在尽力做到知其然知其所以然。二:关于linux交叉编译器的记录** 首先介绍我自己使用最多的 arm-linux-gnueabi-gcc 和 arm-linux-gnueabihf-gcc:**一: 两个交叉编译器分别适用于 armel 和 armhf 两个不同的架构,armel 和 armhf 这两种架构在对待浮点运算采取了不同的策略(有 fpu 的 arm 才能支持这两种浮点运算策略)。**二:**其实这两个_gcc 和 arm-linux-guneabihf

推荐文章

热门文章

相关标签