iOS中UIScrollerView的用法及基于AotoLayout的控件懸停
UIScrollView為了顯示多于一個(gè)屏幕的內(nèi)容或者超過(guò)你能放在內(nèi)存中的內(nèi)容。Scroll View為你處理縮小放大手勢(shì),UIScrollView實(shí)現(xiàn)了這些手勢(shì),并且替你處理對(duì)于它們的探測(cè)和回應(yīng)。其中需要注意的子類是UITableView以及UITextView(用來(lái)顯示大量的文字)。還有一個(gè)UIWebView,盡管那不是UIScrollView的直接子類,它適用UIScrollView去顯示網(wǎng)頁(yè)內(nèi)容contentsize是內(nèi)容的寬和高,contentsize.width是內(nèi)容的寬度,contentsize.heght是高度,contentsize是UIScrollView的一個(gè)屬性,它是一個(gè)CGSize,是由核心圖形所定義的架構(gòu),那定義了你可以滾軸內(nèi)容的寬度和高度,你也可以添加可以上下滾動(dòng)的額外區(qū)域。第一種方法是你可以通過(guò)添加內(nèi)容的大小來(lái)完成。另外一個(gè)比較動(dòng)態(tài)的選擇是UIScrollView的另一個(gè)屬性contentInset,contentInset增加你在contentsize中指定的內(nèi)容能夠滾動(dòng)的上下左右區(qū)域數(shù)量contentInset.top以及contentInset.buttom分別表示上面和下面的距離。

在滾軸視圖中,有一個(gè)叫做ContentOffset的屬性跟蹤UIScrollView的具體位置,你能夠自己獲取和設(shè)置它,ContentOffset是你當(dāng)前可視內(nèi)容在滾軸視圖邊界的左上角那個(gè)點(diǎn)。如圖:

可以看出,ContentOffset內(nèi)容中的那個(gè)點(diǎn)不是從contentInset的左上角開(kāi)始的,而是內(nèi)容的左上角,此時(shí)的ContentOffset是正值,但有時(shí)也是負(fù)值,如下圖所示:

使用一個(gè)ScrollView
創(chuàng)建一個(gè)UIScrollView
CGRectframe = CGRectMake( 0, 0, 200, 200);
scrollView= [[UIScrollView alloc] initWithFrame: frame];
添加子視圖(框架可以超過(guò)scroll view的邊界)
frame= CGRectMake( 0, 0, 500, 500);
myImageView= [[UIImageView alloc] initWithFrame: frame];
[scrollViewaddSubview:myImageView];
設(shè)置內(nèi)容尺寸
scrollView.contentSize= CGSize(500,500);
擴(kuò)展Scroll View 的行為
- 應(yīng)用程序通常需要知道有關(guān)的滾圖的事件
- scrolloffset改變的時(shí)候
- 拖動(dòng)開(kāi)始和結(jié)束
- 減速的開(kāi)始和結(jié)束
通過(guò)子類化擴(kuò)展Scroll View 的行為
- 創(chuàng)建一個(gè)子類
- 重寫(xiě)一些功能并改變行為
- 關(guān)于這種方式的爭(zhēng)議
應(yīng)用程序的邏輯和行為變成了視圖本身的一部分,就像,你可能有一些定制的滾軸邏輯,,在那你只在意一個(gè)視圖控制,但你想在不同地方重復(fù)使用你的滾軸視圖,如果你必須為每個(gè)都子類化,你最后會(huì)有很多不同的滾軸視圖子類以及在視圖中的特定應(yīng)用邏輯。
編寫(xiě)很多子類是很沉悶的事情,你最后會(huì)有很多無(wú)法重復(fù)使用的單獨(dú)視圖,而MVC的視圖部分的一個(gè)重點(diǎn)是視圖是可以在不同的控制器和不同的模式之中重復(fù)使用的,如果我們把所有邏輯都放在視圖中,它減少了可復(fù)用性。
你的代碼變得很牢固地配對(duì)在一起,它實(shí)際上變成了超類的一部分,你無(wú)法從UIScrollView中析取它,之后用其它東西代替,如果它在你控制器中且為控制器的一部分,在之后更容易改變它工作的方式和重新安排你應(yīng)用程序的一些部分。
通過(guò)委派來(lái)擴(kuò)展Scroll View 的行為(常用的)
委派是一個(gè)單獨(dú)的對(duì)象,協(xié)議,定義了委派會(huì)實(shí)現(xiàn)的一系列功能的Objective-C協(xié)議,它創(chuàng)建了一系列很清晰的撤銷點(diǎn),在那里你能定制行為和外觀。它在這些對(duì)象之間保持了松散的配對(duì),視圖本身與視圖控制器或任何其它的控制器對(duì)象,委派不是滾軸視圖的直接子類,它比起牢固配對(duì)的子類更加的松散。
理論篇
在滾動(dòng)的過(guò)程中,實(shí)際上就是contentOffset的值在不斷變化,當(dāng)手指觸摸后,UIScrollView會(huì)暫時(shí)攔截觸摸事件,使用一個(gè)計(jì)時(shí)器。假如在計(jì)時(shí)器到點(diǎn)后沒(méi)有發(fā)生手指移動(dòng)事件,那么UIScrollView發(fā)送 tracking events 到被點(diǎn)擊的 subview 上面。如果在計(jì)時(shí)器到點(diǎn)前發(fā)生了移動(dòng)事件,那么UIScrollView取消 tracking 然后自己發(fā)生滾動(dòng)。
可以重載子類
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view;
決定自己是否接受 touch 事件
- (BOOL)touchesShouldCancelInContentView:(UIView *)view;
開(kāi)始發(fā)送 tracking messages 給 subview 的時(shí)候調(diào)用這個(gè)方法,決定是否發(fā)送 tracking messages 消息到 subview。
返回NO -> 發(fā)送,表示不取消
返回YES -> 不發(fā)送,表示會(huì)取消
屬性篇
@property(nonatomic,readonly,getter=isTracking) BOOL tracking;
當(dāng) touch 后還沒(méi)有拖動(dòng)的時(shí)候值是YES,否則NO
@property(nonatomic,readonly,getter=isZoomBouncing) BOOL zoomBouncing;
當(dāng)內(nèi)容放大到最大或者最小的時(shí)候值是YES,否則NO
@property(nonatomic,readonly,getter=isZooming) BOOL zooming;
當(dāng)正在縮放的時(shí)候值是YES,否則NO
@property(nonatomic,readonly,getter=isDecelerating) BOOL decelerating;
當(dāng)滾動(dòng)后,手指放開(kāi)但是還在繼續(xù)滾動(dòng)中。這個(gè)時(shí)候是YES,其他時(shí)候是NO
@property(nonatomic) CGFloat decelerationRate;
設(shè)置手指放開(kāi)后的減速率
@property(nonatomic) CGFloat maximumZoomScale;
表示放大的最大倍數(shù)
@property(nonatomic) CGFloat minimumZoomScale;
表示縮小的最小倍數(shù)
@property(nonatomic,getter=isPagingEnabled) BOOL pagingEnabled;
當(dāng)值為YES的時(shí)候,就會(huì)產(chǎn)生翻頁(yè)那種效果
@property(nonatomic,getter=isScrollEnabled) BOOL scrollEnabled;
決定是否可以滾動(dòng)
@property(nonatomic) BOOL delaysContentTouches;
當(dāng)值為YES的時(shí)候,用戶一旦觸碰,然后再一定時(shí)間內(nèi)沒(méi)有移動(dòng),UIScrollView會(huì)發(fā)送 tracking events,然后用戶移動(dòng)手指足夠長(zhǎng)度觸發(fā)滾動(dòng)事件,這個(gè)時(shí)候,UIScrollView發(fā)送了-(void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event到 subview,然后UIScrollView開(kāi)始滾動(dòng)。假如值為NO,UIScrollView發(fā)送 tracking events 后,就算用戶移動(dòng)手指,UIScrollView也不會(huì)滾動(dòng)。
@property(nonatomic) BOOL showsHorizontalScrollIndicator;
滾動(dòng)時(shí)是否顯示水平滾動(dòng)條
@property(nonatomic) BOOL showsVerticalScrollIndicator;
滾動(dòng)時(shí)是否顯示垂直滾動(dòng)條
@property(nonatomic) BOOL bounces;
默認(rèn)是YES,就是滾動(dòng)超過(guò)邊界會(huì)有反彈回來(lái)的效果,如果設(shè)置為NO,那么滾動(dòng)到邊界就會(huì)立刻停止
@property(nonatomic) BOOL bouncesZoom;
這個(gè)效果反映在縮放上面,如果縮放超過(guò)最大縮放,就會(huì)有反彈效果,加入設(shè)置為NO,則達(dá)到最大或者最小的時(shí)候立刻停止
@property(nonatomic,getter=isDirectionalLockEnabled) BOOL directionalLockEnabled;
默認(rèn)是NO,可以在垂直和水平方向同時(shí)運(yùn)動(dòng)。當(dāng)值為YES的時(shí)候,加入一開(kāi)始是垂直或者水平運(yùn)動(dòng),那么接下來(lái)會(huì)鎖定另外一個(gè)方向的滾動(dòng)。加入一開(kāi)始是對(duì)角方向滾動(dòng),則不會(huì)禁止某個(gè)方向
@property(nonatomic) UIScrollViewIndicatorStyle indicatorStyle;
滾動(dòng)條的樣式,基本只是設(shè)置顏色
@property(nonatomic) UIEdgeInsets scrollIndicatorInsets;
設(shè)置滾動(dòng)條的位置
方法篇
直接上代碼看
#pragma mark UIScrollViewDelegate
//只要滾動(dòng)了就會(huì)觸發(fā)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
{
// NSLog(@" scrollViewDidScroll");
NSLog(@"ContentOffset x is %f,yis %f",scrollView.contentOffset.x,scrollView.contentOffset.y);
}
//開(kāi)始拖拽視圖
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
{
NSLog(@"scrollViewWillBeginDragging");
}
//完成拖拽
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
{
NSLog(@"scrollViewDidEndDragging");
}
//將開(kāi)始降速時(shí)
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;
{
NSLog(@"scrollViewWillBeginDecelerating");
}
//減速停止了時(shí)執(zhí)行,手觸摸時(shí)執(zhí)行
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
{
NSLog(@"scrollViewDidEndDecelerating");
}
//滾動(dòng)動(dòng)畫(huà)停止時(shí)執(zhí)行,代碼改變時(shí)出發(fā),也就是setContentOffset改變時(shí)
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView;
{
NSLog(@"scrollViewDidEndScrollingAnimation");
}
//設(shè)置放大縮小的視圖,要是uiscrollview的subview
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;
{
NSLog(@"viewForZoomingInScrollView");
return viewA;
}
//完成放大縮小時(shí)調(diào)用
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
viewA.frame=CGRectMake(50,0,100,400);
NSLog(@"scale between minimum and maximum. called after any 'bounce' animations");
}// scale between minimum and maximum. called after any 'bounce' animations
//如果你不是完全滾動(dòng)到滾軸視圖的頂部,你可以輕點(diǎn)狀態(tài)欄,那個(gè)可視的滾軸視圖會(huì)一直滾動(dòng)到頂部,那是默認(rèn)行為,你可以通過(guò)該方法返回NO來(lái)關(guān)閉它
- (BOOL)scrollViewShouldScrollToTop:(UIScrollView *)scrollView;
{
NSLog(@"scrollViewShouldScrollToTop");
returnYES;
}
- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView;
{
NSLog(@"scrollViewDidScrollToTop");
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
@end
基于AotoLayout的控件懸停
在很多的電商類的APP中我們經(jīng)常會(huì)用到一種滑動(dòng)懸停的效果,所以這次嘗試著利用AutoLayout來(lái)實(shí)現(xiàn)這種效果。話不多說(shuō)先上圖
效果:

效果上過(guò)了,還是理順下思路上吧。從效果圖上不難發(fā)現(xiàn)我們的程序是基于UIScrollerView的。界面上的展示同樣都是UIScrollView的子控件。當(dāng)然了難點(diǎn)也就是基于UIScrollerView的內(nèi)部子控件的布局以及AutoLayout約束的修改。
我們看一下我們的需要的一些基本的控件布局:

好了,現(xiàn)在開(kāi)始開(kāi)始搭建新的項(xiàng)目了,首先在Storyboard上搭建最基本的布局,并將控件基本布局到相應(yīng)的位置,以備我們更方便的添加約束。

到了這里就開(kāi)始對(duì)我們的控件添加基本的約束了從上開(kāi)始吧。上面的控件我們能確定是UIImageView的heigth以及top、left以及rihgt。至于bottom到了后面會(huì)著重設(shè)置的。

然后繼續(xù)來(lái)操作懸停的View。這個(gè)View因?yàn)樘幵趦蓚€(gè)view的中間所以我們要暫時(shí)確定height以及l(fā)eft和right的約束就足夠了了

終于到最后一個(gè)UIImageView了,同樣的能確定的是height、right、left以及bottom這四項(xiàng)。

到了這里針對(duì)單個(gè)的控件的布局基本完成了,細(xì)心的你可能會(huì)發(fā)現(xiàn)以上的操作都沒(méi)有進(jìn)行關(guān)聯(lián)以及沒(méi)有對(duì)width進(jìn)行約束。那么下面我們就要開(kāi)始進(jìn)行三者的關(guān)聯(lián)以及width上的設(shè)置。
因?yàn)閼彝5膙iew處與兩個(gè)imageView的中間,所以我們只需要對(duì)它進(jìn)行處理就好了

其實(shí)這兩個(gè)約束完全是可以在設(shè)置懸停view的時(shí)候的添加,之所以在這里添加是我們?cè)诤竺娴目刂破鲿?huì)用到這兩個(gè)約束,為了好辨認(rèn)我們就給他們定義上別名:

然后我們按住command選擇我們的三個(gè)控件然后按住control拖向scrollView的父控件view設(shè)置等width。

好了,到了這里我們的約束已經(jīng)不會(huì)再報(bào)錯(cuò)了,然后在邏輯上也是基本是通順的了,但是對(duì)于我們懸停的功能來(lái)說(shuō)就少了關(guān)鍵的一個(gè)環(huán)節(jié)那么就是一旦懸停view懸停了,那么它與上下兩個(gè)imageView的約束要怎么處理呢?所以我們還需要一個(gè)輔助的約束用來(lái)懸停之后的關(guān)聯(lián)處理

添加之后我們給這個(gè)約束起個(gè)別名hidden懸停,不過(guò)細(xì)心的你一定會(huì)發(fā)現(xiàn)添加之后會(huì)sb會(huì)提示出有沖突的約束,那么我們只需要對(duì)我們剛剛添加的約束進(jìn)行一下操作就OK了

好了,到了這里我們?cè)贏utoLayout上要做的事情就這些了,下面我們就可以安心的寫(xiě)代碼了。
@interface ViewController ()/**中間的View*/@property (weak, nonatomic) IBOutlet UIView *centreView;/**上面的ImageView*/@property (weak, nonatomic) IBOutlet UIImageView *topImageView;/**centreView的top約束*/@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topConstraint;/**centreView的bottom約束*/@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;/**topImageView暫禁用的約束*/@property (weak, nonatomic) IBOutlet NSLayoutConstraint *hiddenConstraint;
@end
然后在scrollView的滑動(dòng)代理方法中實(shí)現(xiàn)
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
//獲得top圖片的高度
CGFloat imageH = self.topImageView.frame.size.height;
//獲取偏移量
CGFloat offsetY = scrollView.contentOffset.y;
//centrView的frame
CGRect centreFrame = self.centreView.frame;
if (offsetY>=imageH) {
//將centreView的向上和向下的約束禁用
self.bottomConstraint.active = NO;
self.topConstraint.active = NO;
//將topImageView與bottomImageView的約束使用關(guān)聯(lián)
self.hiddenConstraint.active = YES;
//懸停在位置
centreFrame.origin.y = 0;
self.centreView.frame = centreFrame;
//添加在scrollView的父控件
[self.view addSubview:self.centreView];
}else{
//添加在topIamgeView的下面
centreFrame.origin.y = imageH;
self.centreView.frame = centreFrame;
[scrollView addSubview:self.centreView];
//記住一定要先添加到scrollView上之后在修改約束的內(nèi)容,不然添加的約束會(huì)不成,因?yàn)橄到y(tǒng)無(wú)法建立他們之間的聯(lián)系。
self.hiddenConstraint.active = NO;
self.topConstraint.active = YES;
self.bottomConstraint.active = YES;
}
//等比例的伸縮
CGFloat scale= 1-(offsetY/60);
scale = (scale>=1)?scale :1;
self.topImageView.transform = CGAffineTransformMakeScale(scale, scale);
}
最后補(bǔ)充:
在子控件的尺寸不能通過(guò)UIScrollView來(lái)計(jì)算,可以通過(guò)以下方式計(jì)算
- 設(shè)置固定值(width==100,height==300)
- 相對(duì)于UIScrollView以外的其他控件來(lái)計(jì)算尺寸
在UIScrollView里面布局子控件,sb里面默認(rèn)是是需要子控件的尺寸以及子控件與UIScrollView之間的間距來(lái)計(jì)算出scroller的conentSize的。所以這就是上面的子控件要與父控件來(lái)等寬的原因了。
UIScrollView的frame應(yīng)該通過(guò)子控件以外的其他控件來(lái)計(jì)算。
- 深入理解Android中Scroller的滾動(dòng)原理
- Android程序開(kāi)發(fā)之UIScrollerView里有兩個(gè)tableView
- 詳解Android應(yīng)用開(kāi)發(fā)中Scroller類的屏幕滑動(dòng)功能運(yùn)用
- 詳解Android Scroller與computeScroll的調(diào)用機(jī)制關(guān)系
- jQuery滾動(dòng)條插件nanoscroller使用指南
- javascript AutoScroller 函數(shù)類
- android開(kāi)發(fā)之橫向滾動(dòng)/豎向滾動(dòng)的ListView(固定列頭)
- android實(shí)現(xiàn)上下滾動(dòng)的TextView
- Android中實(shí)現(xiàn)多行、水平滾動(dòng)的分頁(yè)的Gridview實(shí)例源碼
- Android Scroller完全解析
相關(guān)文章
iOS之單獨(dú)使用UISearchBar創(chuàng)建搜索框的示例
本篇文章主要介紹了iOS之單獨(dú)使用UISearchBar創(chuàng)建搜索框的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-10-10
iOS中使用URL Scheme進(jìn)行App跳轉(zhuǎn)的教程
這篇文章主要介紹了iOS中使用URL Scheme進(jìn)行App跳轉(zhuǎn)的教程,比如在應(yīng)用內(nèi)提示安裝另一個(gè)應(yīng)用時(shí)就以u(píng)rl打開(kāi)safari然后打開(kāi)app store那樣,需要的朋友可以參考下2016-04-04
IOS開(kāi)發(fā)過(guò)程中的消息通知--小紅點(diǎn)
本文主要介紹了IOS開(kāi)發(fā)過(guò)程中的消息通知--小紅點(diǎn)的相關(guān)知識(shí)。大致分為兩種方法:系統(tǒng)方法和自定義方法。下面跟著小編一起來(lái)看下吧2017-04-04
iOS開(kāi)發(fā)中導(dǎo)航控制器的基本使用教程
這篇文章主要介紹了iOS開(kāi)發(fā)中導(dǎo)航控制器的基本使用教程,代碼基于傳統(tǒng)的Objective-C,需要的朋友可以參考下2015-11-11
iOS開(kāi)發(fā)學(xué)習(xí)之監(jiān)測(cè)程序的崩潰次數(shù)詳解
iOS開(kāi)發(fā)中遇到程序崩潰是很正常的事情,下面這篇文章主要給大家介紹了關(guān)于iOS如何監(jiān)測(cè)程序崩潰次數(shù)的相關(guān)資料,文中通過(guò)詳細(xì)的示例代碼供大家參考學(xué)習(xí),需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-07-07
iOS實(shí)現(xiàn)搭建聊天頁(yè)面的實(shí)例代碼
本篇文章主要介紹了iOS實(shí)現(xiàn)搭建聊天頁(yè)面的實(shí)例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07

