第二章节 UIScrollView
从加载xib中,获取xib资源
在实际项目中,代码配合xib是一个比较好的方式,能够节省很多时间。一些简单的可以确定是固定不变的模型可以种xib直接加载到我们的代码中。加载xib有两种方式
- 直接从文件包中加载
NSArray * objc = [[NSBundle mainBundle] loadNibNamed:@"Filer" owner:nil options:nil];
这种方式加载到我们的程序中返回的是数组形式,也就是说我们可以在一个xib中建立多个模型对象,但是在取模型对象的时候一定要注意顺序
2.利用UINib进行加载
UINib * nib = [UINib nibWithNibName:@"Filer" bundle:[NSBundle mainBundle]];
NSArray * array = [nib instantiateWithOwner:nil options:nil];
同样这种方式加载到程序中返回的依旧是返回数组形式,当bundle的参数传入为nil的时候可以达到同样的效果,我们的程序小哥会自己到主文件资源包中去寻找。
当想取出xib封装的模型中的某一个控件,可以给控件添加tag值。然后用 [UIView viewWithTag:X] 取出控件。但是不建议这样写,因为这样会暴露自己的tag值,暴漏的xib中所拥有的控件。
UIScrollView基本使用
首先新建一个工程
//
// ViewController.m
// JXUIScrollView
//
// Created by 王加祥 on 16/4/12.
// Copyright © 2016年 Wangjiaxiang. All rights reserved.
//
#import "ViewController.h"
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// UIImageView * imageView = [[UIImageView alloc] init];
// imageView.image = [UIImage imageNamed:@"1"];
// imageView.frame = CGRectMake(0, 0, imageView.image.size.width, imageView.image.size.height);
UIImageView * imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"1"]];
[self.scrollView addSubview:imageView];
self.scrollView.contentSize = imageView.image.size;
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)left:(id)sender {
}
- (IBAction)top:(id)sender {
}
- (IBAction)down:(id)sender {
}
- (IBAction)right:(id)sender {
}
@end
为了简便,直接用xib托出一个UIScrollView控件。 之后新建一个UIImageView置于UIScrollView上面。 其中新建UIImageView,可以用一行代码直接新建,并将图片的尺寸完全赋值给UIImageView。
- 如果想禁止UIScrollView某一方向上的滚动,可以设置宽度或者高度方向为0.(宽度设置为0代表水平禁止滚动)
self.scrollView.contentSize = CGSizeMake(240, 400);
- UISCrollView最重要属性之一 contentOffset
- 左边超过UIScrollView为正值,上边超过UIScrollView为正值
contentOffset用处非常多,可以监控UIScrollView内容视图运动的位置
3.UIScrollView中动画效果
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationDelegate:self]; // 代理
[UIView setAnimationDidStopSelector:] // 当动画结束的时候调用的代理方法
[UIView setAnimationWillStartSelector:] // 当动画开始的时候调用的方法
// 将需要运动的代码放到这里就可以运动
self.scrollView.contentOffset = CGPointMake(self.scrollView.contentSize.width - self.scrollView.bounds.size.width , self.scrollView.contentOffset.y);
[UIView commitAnimations];
```
[UIView animateWithDuration:1 delay:1 options:UIViewAnimationOptionCurveLinear animations:^{
} completion:^(BOOL finished) {
}];```
上面两种方法都能简单实现动画效果,实现动画的效果还有很多
- 设置contentInset
这是一个结构体指针,用来设置内容视图的周围的间距大小。 此时控件的内容大小不会变化
设置UIScrollView的代理
- 监听缩放
/**
* 方法的返回值决定了要缩放的内容,只要是控件原则上都是可以缩放的
*/
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.imageView;
}
监听缩放的时候需要设置代理,并且要设置缩放的比例
self.scrollView.maximumZoomScale = 2; self.scrollView.minimumZoomScale = 0.2;
- 监听滚动页数
首先需要设置 scrollView.pagingEnabled = YES;
计算滚动的页数,这里有一个小的计算技巧
int page = (int)(scrollView.contentOffset.x / scrollView.bounds.size.width + 0.5);
UIScrollView
- 初始化代码块
- 一个控件加载有代码和xib两种方式。所以一般封装控件的时候都需要考虑到两种方式。其中两种方式的初始化方式不同,需要执行的代码也是不同。
代码创建的方式需要执行的代码
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
}
return self;
}
xib创建的方式执行的代码
- (void)awakeFromNib {
}
可以根据不同需要,将需要初始化的方法单独封装到一个方法里面,然后在两个需要执行的代码中分别调用
2.定时器
这里我们采用NSTimer方法
NSTimer * timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
[timer fire];
这个方法的缺点是timer是一个局部变量,如果想要在外部调用的话我们需要设置一个全局变量,使用的时候有诸多限制
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
会自动设置一个全局变量,在一个方法的外面执行selector方法依然有效。
3.定时器完善
当我们采用上面方法创建一个定时器,来控制我们的图片滚动。
- (void)nextPage {
NSLog(@"nextPage");
NSInteger page = self.pageControl.currentPage + 1;
if (page == self.pageControl.numberOfPages) {
page = 0;
}
CGPoint offest = self.scrollView.contentOffset;
offest.x = page * self.scrollView.frame.size.width;
[self.scrollView setContentOffset:offest animated:YES];
}
以上的代码就是定时器调用的方法来控制图片滚动的方法,但是有一个缺点就是当我们手指开始进行拖动的时候此时会阻塞定时器方法,但是定时器并没有消失,而是一直在“积累”,当我们松开手指的时候图片会滚动的非常快。下面我哦们来完善定时器方法,继续监听UIScrollView的代理方法,设置一个定时器全局变量。
- (void)setup {
[self startTimer];
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
[self stopTimer];
}
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
[self startTimer];
}
#pragma mark - 定时器方法
- (void)stopTimer {
[self.timer invalidate];
self.timer = nil; // 定时器销毁之后一定要置空。这样才安全
}
- (void)startTimer {
// 给定一个定时器
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(nextPage) userInfo:nil repeats:YES];
// 将定时器添加到主线程中,设置为通用模式,与其他代码相当于同时执行
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
添加以上的完善的方法之后就可以拖动,自动滚动。
Autolayout
适配:在移动开发中常见的适配有两种,一种是系统适配,一种是屏幕适配
- 屏幕适配的发展历史
iPhone3GS \ iPhone4
- 没有屏幕适配可言
- 全部用frame,bounds,center进行布局
- 很多坐标,宽度高度等完全写死
iPad出现,iphone横屏
- 出现Autoresizing技术(相比Autolayout功能少了许多)
- 让横竖屏适配更简单
- 让子控件可以跟随父控件的行为发生相应的变化
- 使用Autoresizing必须关闭Autolayout
- 出现Autoresizing技术(相比Autolayout功能少了许多)
- (void)viewDidLoad {
[super viewDidLoad];
UIView * blueView = [[UIView alloc] init];
blueView.backgroundColor = [UIColor blueColor];
CGFloat wh = 100;
CGFloat x = self.view.frame.size.width - wh;
CGFloat y = self.view.frame.size.height -wh;
blueView.frame = CGRectMake(x, y, 100, 100);
// 在添加到父控件之前设置
// 子控件永远在父控件的右下角
blueView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin;
/*
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleTopMargin = 1 << 3, 设置这个代表在xib中设置约束不打钩
UIViewAutoresizingFlexibleLeftMargin = 1 << 0, 设置这个代表在xib中设置约束不打钩
UIViewAutoresizingFlexibleBottomMargin = 1 << 5 设置这个代表在xib中设置约束不打钩
UIViewAutoresizingFlexibleRightMargin = 1 << 2, 设置这个代表在xib中设置约束不打钩
UIViewAutoresizingFlexibleHeight = 1 << 4, 高度跟随父控件的高度进行自动伸缩 设置这个属性代表xib中约束打钩
UIViewAutoresizingFlexibleWidth = 1 << 1, 宽度跟随父控件的宽度进行自动伸缩 设置这个属性代表xib中约束打钩
*/
[self.view addSubview:blueView];
}
iOS 6.0 (Xcode4)开始
- 出现了Autolayout技术
- 从Xcode5.0(iOS7.0)开始普遍使用
Autolayout的两个核心概念
- 参照
- 约束
Autolayout在约束的过程中可能会有两种警告,一种是警告,一种是错误
- 当为警告的黄色按钮的时候需要更新一下frame
- 当为红色错误的时候可能的问题是约束冲突,也可能是约束不够,没有完全约束
UIScrollView项目中运用
悬停效果
在有些项目中好像是一个类似于TableView的headerView的一个效果,随着视图的滚动上下滚动,当这个视图滚动到了顶部的时候就会悬停,然后下面的继续滚动
这种效果可以用UIScrollView来做,其中就是将一个ScrollView添加到控制器view上,然后在ScrollView上添加各种控件,当需要悬停的控件滚动到指定位置的时候是,就将这个需要悬停的控件的父控件设置为控制器的view,然后离开这个位置的时候继续将这个需要悬停的控件的父控件设置ScrollView即可
这里更换父控件的时候,不需要先将控件从之前的父控件中移除
UIScrollView在xib中的布局
在storyboard中添加一个UIScrollView之后,在其上添加子控件的时候,子控件的布局有一个坑
在布局子控件的时候,处理设置子控件距离父控件(UIScrollView)上下左右的距离
还需要设置子控件的width以及height(可以跟UIScrollView的父控件建立关系)。
因为UIScrollView需要在storyboard中计算它的contentsize
UIScrollView内部子控件添加约束的注意点:
- 子控件的尺寸不能通过UIScrollView来计算,可以考虑通过以下方式计算
- 可以设置固定值(width==100,height==300)
- 可以相对于UIScrollView以外的其他控件来计算尺寸
- UIScrollView的frame应该通过子控件以外的其他控件来计算
- UIScrollView的contentSize通过子控件来计算
- 根据子控件的尺寸以及子控件与UIScrollView之间的间距