第2章 UI控件
学习目标
掌握什么是UIView及UIView提供的常见属性和方法。
掌握常见UI控件的使用,能够灵活使用控件开发iOS应用。
一个用户体验良好的应用,都离不开友好的图形用户界面。iOS应用开发的一项重要内容就是用户界面的开发,iOS提供了大量功能丰富的UI控件,开发者只要按一定规律将这些UI控件组合起来,就可以开发出优美的图形用户界面。本章将针对iOS中的UI控件进行详细讲解。
2.1 UIView概述
2.1.1 什么是UIView
在iOS开发中,每个UI控件都相当于一个个小的积木,这些控件都继承了UIView。曾经有人这么说过,在iPhone中,你看到的、摸到的都是UIView,例如,按钮、图片、文字等。既然UIView是所有控件的父控件,那么它必定拥有很多子控件,接下来,通过一张图来描述UIView的继承体系结构,如图2-1所示。
图2-1 UIView继承体系图
从图2-1中可以看出,UIView继承体系中的类具有不同的层次关系,例如,UIView继承自UIResponder,它提供了许多子类,如UILabel、UITabBar、UIPickerView、UIControl等。其中,UIControl类又提供了子类 UIButton、UITextField、UITextView等。通常情况下,继承UIView的子类对象也称为视图。
为了便于大家更好地理解这些类,接下来,通过一个应用程序界面来讲解这些类的具体应用,如图2-2所示。
图2-2 应用程序界面
从图2-2中可以看出,应用程序的界面都是由一个一个的控件组成的,这些控件对应的是不同的类,这些类在后面的章节中都会进行详细讲解,这里大家有个大致印象即可。
2.1.2 UIView的常见属性和方法
在开发iOS程序时,经常需要修改UI控件的显示状态,例如,文件下载的进度条是实时更新的,论坛访问人数也是实时变化的,这些UI控件状态的修改,其实就是通过修改UI控件属性实现的。虽然不同的UI控件都有自己独特的属性,但某些属性是每个UI控件都具备的。UIView提供了许多公共的属性,例如,每个UI控件都有自己的位置和尺寸,每个UI控件都有父控件、子控件。接下来,通过一张表来列举UIView的常见属性,见表2-1。
表2-1 UIView的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic,readonly) UIView *superview; |
用于获得自己的父控件对象 |
@property(nonatomic,readonly,copy)NSArray *subviews; |
用于获取自己所有的子控件对象 |
@property(nonatomic) CGAffineTransform transform; |
用于表示控件的形变属性(可以设置旋转角度、比例缩放、平移等属性) |
@property(nonatomic) NSInteger tag; |
用于表示控件的ID(标识),父控件可以通过tag来找到对应的子控件 |
@property(nonatomic) CGRect frame; |
控件所在矩形框在父控件中的位置和尺寸(以父控件的左上角为坐标原点) |
@property(nonatomic) CGRect bounds; |
用于表示控件所在矩形框的位置和尺寸(以自己左上角为坐标原点) |
@property(nonatomic) CGPoint center; |
控件中点的位置(以父控件的左上角为坐标原点) |
@property(nonatomic) CGFloat alpha; |
用于控制控件的透明度,其值支持0.0~1.0的任意浮点数值 |
表2-1列举了UIView的常见属性,其中,bounds属性是以自己左上角为坐标原点定义控件所在矩形框的位置和尺寸,因此,它可以实现控件大小的定义;center属性则是以父控件的左上角为坐标原点定义控件中点的位置,它可以实现控件位置的定义;而tag属性则可以定义控件的唯一标识,用于程序获取该控件的引用。
除此之外,UIView还提供了许多常见的方法,见表2-2。
表2-2 UIView的常见方法
方法声明 |
功能描述 |
---|---|
- (void)addSubview:(UIView *)view; |
用于添加一个子控件view |
- (void)removeFromSuperview; |
用于从父控件中移除 |
- (UIView *)viewWithTag:(NSInteger)tag; |
根据控件的tag标识找出对应的控件 |
表2-2列举了UIView的3个常见方法,其中,addSubview方法用于向当前视图添加一个子视图,并将该子视图的保留计数加1;removeFromSuperview方法会从父视图中移除当前视图,并将当前视图的保留计数减1;为了方便查找控件,通常情况下,我们都会为每个控件添加一个Tag,如果想根据Tag查找对应的控件,则可以使用viewWithTag方法来实现。
脚下留心:UIKit坐标系
在实际开发中,经常需要对视图进行各种变换,例如,缩放、平移、旋转等。而在进行这些操作之前,都需要明确当前视图的位置和尺寸,而这些数值都与UIKit坐标系有关。接下来,通过一张图来描述UIKit是如何定义坐标系的,如图2-3所示。
图2-3 UIKit坐标系
从图2-3中可以看出,UIKit坐标系中的原点位于左上角,横坐标正方向水平向右延伸,纵坐标正方向竖直向下延伸,并且图中所示的iPhone的分辨率大小为320×480。
2.2 标签控件和图片控件
UIView提供了许多子类,这些子类分别代表不同的控件。在众多的控件中,有些控件几乎在每个应用程序中都会用到,例如,用于文本显示的标签控件、用于展示图片的图片控件,接下来,本节将针对这两个最简单的控件进行详细讲解。
2.2.1 标签控件(UILabel)
在iOS开发中,控件标签是使用UILabel类表示的,它直接继承自UIView类,是一个用于显示文字的静态控件。默认情况下,标签控件是不能接受用户输入,也不能与用户交互的。为了大家更好地理解什么是标签控件,接下来,通过一张图来描述标签控件的应用场景,如图2-4所示。
图2-4 标签控件的应用
图2-4所示是一个用户注册的界面,该图中使用了多个标签控件用于显示固定的文字。由此可见,标签控件的使用是非常频繁的。
要想在程序中使用标签控件,首先得学会创建标签控件。创建标签控件最简单的方式就是将对象库中的Label控件直接拖曳到Main.storyboard编辑界面中,如图2-5所示。
图2-5 将Label控件拖曳到程序界面
从图2-5中可以看出,使用拖曳控件的方式可以轻而易举地完成Label控件的创建,这时,选中Label控件,在Xcode右侧会出现UILabel的属性检查器面板,用于对Label控件进行设置,如图2-6所示。
图2-6 UILabel的属性检查器面板
图2-6所示标识出了UILabel控件的一些属性设置,这些属性设置均可改变UILabel控件的显示状态。同时,针对属性检查器面板中的设置,UILabel还提供了相应的属性,接下来,通过一张表来列举UILabel控件的常见属性,见表2-3。
表2-3 UILabel的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic,copy) NSString*text; |
设置显示的文本内容,默认为nil |
@property(nonatomic,retain) UIFont*font; |
设置字体和字体大小,默认为系统字体17号 |
@property(nonatomic,retain) UIColor *textColor; |
设置文本的颜色,默认为黑色 |
@property(nonatomic,retain) UIColor*shadowColor; |
设置文本的阴影色彩和透明度,默认为nil |
@property(nonatomic)CGSize shadowOffset; |
设置阴影纵横向的偏移量,默认为CGSizeMake(0, -1),即为顶部阴影 |
@property(nonatomic) NSTextAlignment textAlignment; |
设置文本在标签内部的对齐方式,默认为NSTextAlignmentLeft,即为左对齐 |
@property(nonatomic) NSLineBreakMode lineBreakMode; |
指定换行模式,模式为枚举类型 |
@property(nonatomic) NSInteger numberOfLines; |
指定文本行数,为0时没有最大行数限制 |
表2-3列举了UILabel控件的常见属性,其中text属性支持两种文本设置方式,分别是Plain和Attributed;font属性用于设置UILabel显示的字体样式及大小;shadowOffset用于设置控件内的阴影文本与正常文本之间的偏移,该属性需要指定Horizontal和Vertical两个值,分别表示阴影文本与正常文本在水平和垂直方向的偏移距离。
为了大家更好地掌握标签控件的属性,接下来,创建一个Single View Application应用,命名为01_UILabel,进入viewController.m文件,通过纯粹的代码方式来创建一个标签控件,并且使用UILabel提供的属性对标签进行设置,代码如例2-1所示。
【例2-1】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 @end
4 @implementation ViewController
5 -(void)viewDidLoad {
6 [super viewDidLoad];
7 // 1.初始化标签控件
8 UILabel *label = [[UILabel alloc] init];
9 // 2.设置标签控件的frame
10 CGFloat labelX = 0; // X值为0
11 CGFloat labelY = 210; // Y值为210
12 CGFloat labelW = self.view.bounds.size.width; //width值为屏幕宽度
13 CGFloat labelH = 120; // height值为40
14 CGRect frame = CGRectMake(labelX, labelY, labelW, labelH);
15 label.frame = frame;
16 // 3.设置背景颜色为灰色
17 label.backgroundColor = [UIColor lightGrayColor];
18 // 4.设置字体和字体颜色
19 label.text = @"Welcome to itcast传智播客";
20 label.font = [UIFont fontWithName:@"Helvetica-Bold" size:40];
21 label.textColor = [UIColor whiteColor];
22 // 5.设置对齐方式为居中
23 label.textAlignment = NSTextAlignmentCenter;
24 // 6.设置阴影
25 label.shadowColor = [UIColor colorWithWhite:0.1f alpha:0.8f];
26 label.shadowOffset = CGSizeMake(2, 3);
27 // 7.设置换行
28 label.lineBreakMode = NSLineBreakByWordWrapping;
29 label.numberOfLines = 2;
30 // 显示到UIView
31 [self.view addSubview:label];
32 }
33 @end
程序的运行结果如图2-7所示。
在例2-1中,创建标签控件和设置属性的具体代码都是在viewDidLoad方法中实现的。这是因为程序的视图控制器完成视图的加载后,会自动调用viewDidLoad方法对子控件views进行进一步的初始化。从图2-7中可以看出,程序成功创建了一个特定样式的标签控件。
2.2.2 图片控件(UIImageView)
友好的用户界面,离不开丰富的图片。图片控件是用UIImageView类表示的,它直接继承于UIView类,是一个用于显示图片的静态控件。例如,应用程序的下载界面,包含了多个图片控件,如图2-8所示。
图2-7 运行结果
图2-8 应用下载界面
图2-8所示是一个应用程序下载的界面,该界面包含了多个图片控件,这些图片控件都只有展示的功能,并不能实现与用户的交互功能。
要想在程序中使用UIImageView显示图片,最简单的方式也是直接将对象库中的Image View拖曳到程序界面中。当选中该控件后,在Xcode右侧会出现UIImageView的属性检查器面板,该面板用于修改UIImageView的显示状态,如图2-9所示。
图2-9 UIImageView的属性检查器面板
在图2-9显示的UIImageView属性器面板中,其中,Mode属性是继承自UIView的,该属性可以控制UIImageView显示图片的缩放模式,单击该属性的下拉列表,如图2-10所示。
图2-10 Mode属性列表框
从图2-10所示,Mode属性包含了很多选项,这些选项所代表的含义如下所示。
- Scale To Fill:不保持纵横比缩放图片,使图片完全适应该UIImageView控件。
- Aspect Fit:保持纵横比缩放图片,使图片的长边能完全显示,即可以完整地展示图片。
- Aspect Fill:保持纵横比缩放图片,只能保证图片的短边能完成显示出来,即图片只能在水平或者垂直方向是完整的,另一个方向会发生截取。
- Center:不缩放图片,只显示图片的中间区域。
- Top:不缩放图片,只显示图片的顶部区域。
- Bottom:不缩放图片,只显示图片的底部区域。
- Left:不缩放图片,只显示图片的左边区域。
- Right:不缩放图片,只显示图片的右边区域。
- Top Left:不缩放图片,只显示图片的左上边区域。
- Top Right:不缩放图片,只显示图片的右上边区域。
- Bottom Left:不缩放图片,只显示图片的左下边区域。
- Bottom Right:不缩放图片,只显示图片的右下边区域。
针对UIImageView属性检查器面板中的设置,UIImageView类也定义了相应的属性,接下来,通过一张表来列举UIImageView的常见属性,见表2-4。
表2-4 UIImageView的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic,retain) UIImage *image; |
访问或设置控件显示的图片 |
@property(nonatomic,retain) UIImage *highlightedImage; |
设置高亮状态下显示的图片 |
@property(nonatomic,getter=isUserInteractionEnabled) BOOL userInteractionEnabled; |
设置是否允许用户交互,默认不允许用户交互 |
@property(nonatomic,getter=isHighlighted) BOOL highlighted; |
设置是否高亮状态,默认为普通状态 |
@property(nonatomic,copy) NSArray *animationImages; |
设置序列帧动画的图片数组 |
@property(nonatomic,copy) NSArray *highlightedAnimationImages; |
设置高亮状态下序列帧动画的图片数组 |
@property(nonatomic) NSTimeInterval animationDuration; |
设置序列帧动画播放的时长 |
@property(nonatomic) NSInteger animationRepeatCount; |
设置序列帧动画播放的次数 |
表2-4列举了UIImageView常见的属性,其中,前4个属性是用来设置图片状态的,后4个属性是用来设置图片动画的。这些属性都是操作UIImageView最常用到的,在后面的小节中,将针对这些属性的使用进行详细讲解。
由于UIImageView可以实现序列帧动画显示一组图片,因此,UIImageView除了提供实现动画的相关属性外,还提供了实现序列帧动画的相关方法,接下来,通过一张表来列举UIImageView关于序列帧动画播放的相关方法,见表2-5。
表2-5 UIImageView用于播放帧动画的相关方法
方法声明 |
功能描述 |
---|---|
- (void)startAnimating; |
开始播放动画 |
- (void)stopAnimating; |
停止播放动画 |
- (BOOL)isAnimating; |
判断是否正在播放动画 |
表2-5中列举了3个方法,这3个方法都是用来控制动画播放状态的。
2.2.3 实战演练——会喝牛奶的汤姆猫
相信大家都知道一个比较好玩的应用叫“会说话的汤姆猫”,该应用中的汤姆猫不仅可以模仿人说话,还可以指挥汤姆猫进行各种动作,如喝牛奶、挠抓屏幕、挨打等动作。其实,这些动作都是一系列图片的动画效果,接下来,我们以喝牛奶为例,带领大家使用图片控件来开发一个会喝牛奶的汤姆猫,具体步骤如下。
1.创建工程,设计界面
(1) 新建一个Single View Application应用,名称为02_UIImageView,然后在Main.storyboard界面中添加一个UIImageView控件和一个UIButton控件,其中,UIImageView控件用于显示汤姆猫,UIButton控件用于显示牛奶瓶。
(2) 将提前准备好的图片分别放到Supporting Files和Images.xcassets文件中,并为
UIImageView控件和UIButton控件设置背景图片,设计好的界面如图2-11所示。
图2-11 喝牛奶的汤姆猫界面
2.创建控件对象的关联
(1)单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,选中UIButton控件,添加一个单击事件,命名为drink,如图2-12所示。
图2-12 创建UIButton控件对象的关联
(2)同样的方式,选中UIImageView控件,添加一个表示汤姆猫的对象,命名为tom,如图2-13所示。
图2-13 创建UIImageView对象的关联
3. 通过代码实现汤姆猫喝牛奶的功能
完成控件对象的关联后,就可以通过代码实现汤姆猫喝牛奶的功能了。进入ViewController.m文件,在drink方法中加载图片,并且设置喝牛奶的一系列动画,代码如例2-2所示。
【例2-2】ViewController.m
1 #import"ViewController.h"
2 @interface ViewController ()
3 // 声明一个表示汤姆猫的属性tom
4 @property (weak, nonatomic) IBOutlet UIImageView *tom;
5 // 声明一个喝牛奶的方法drink
6 -(IBAction)drink:(id)sender;
7 @end
8 @implementation ViewController
9 -(IBAction)drink:(id)sender {
10 // 1.加载所有的动画图片
11 NSMutableArray *images=[NSMutableArray array];
12 for(int i=0;i<81;i++){
13 // 计算文件名
14 NSString *filename=[NSString stringWithFormat:@"drink_%02d.jpg",i];
15 // 加载图片
16 UIImage *image=[UIImage imageNamed:filename];
17 // 添加图片到数组中
18 [images addObject:image];
19 }
20 self.tom.animationImages=images;
21 //2.设置播放次数(1次)
22 self.tom.animationRepeatCount=1;
23 // 3.设置播放时间
24 self.tom.animationDuration=8;
25 // 4.开始播放动画
26 [self.tom startAnimating];
27 }
28 @end
在例2-2中,第11行代码创建了一个数组,用于存放一系列动画图片;第12~18行代码用于将存放在指定目录下的图片添加到数组中;第20行代码用于设置动画图片;第22~24行代码用于设置动画播放的次数及其持续时间;第26行代码用于播放动画。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,单击喝牛奶的图标,一个“会喝牛奶的汤姆猫”应用成功开发完成了。汤姆猫喝牛奶的部分场景如图2-14所示。
图2-14 会喝牛奶的汤姆猫
注意:
PNG格式的图片资源,可以直接放到Images.xcassets文件和Supporting Files文件下;而 JPEG格式的图片资源只能放到Supporting Files文件下。
2.3 按钮控件(UIButton)
2.3.1 按钮控件概述
按钮控件是最常用的控件之一,通常情况下,单击某个控件后,会做出相应反应的都是按钮控件。在iOS开发中,按钮控件使用UIButton类表示的,它直接继承自UIControl:UIView,是一个既能显示文字,又能显示图片,还能随时调整内部图片和文字位置的按钮。例如,播放器界面有很多按钮控件,如图2-15所示。
图2-15 播放器界面中的按钮
图2-15所示的是播放器界面中的一部分,这部分包含了5个按钮控件,这些控件都是可单击的,并且单击这些按钮控件后,会做出不同的反应。
同标签控件、图片控件一样,创建按钮控件最简单的方式也是通过拖曳控件的方式来完成。进入对象库,选中Button控件,在Xcode右侧会出现UIButton的属性检查器面板,如图2-16所示。
图2-16 UIButton的属性检查器面板
图2-16所示的是UIButton所支持的一些属性,其中,Type和State Config属性比较难理解,接下来,针对这两个属性进行详细讲解,具体如下。
1. Type
该属性用于设置按钮的类型,它支持多个选项,单击Type下拉列表,如图2-17所示。
图2-17 按钮控件的Type属性
从图2-17中可以看出,按钮控件的Type属性有6个选项,这6个选项表示的含义如下所示。
- Custom:该选项表示按钮控件是开发者自己定义的,是最常被选用的Type类型。
- System:该选项是iOS默认的按钮风格。
- Detail Disclosure:该按钮通常用于显示当前列表项的详情,最新发布的iOS 8中显示
图标。
- Info Light:显示
图标的图形按钮,该按钮通常用于显示简短的说明信息。
- Info Dark:显示
图标的图形按钮,该按钮通常用于显示简短的说明信息。
- Add Contact:显示
图标的图形按钮,该按钮通常用于显示添加联系人。
2.State Config
该属性用于配置该按钮的状态,它支持多个选项,单击State Config下拉列表,如图2-18所示。
图2-18 按钮控件的State Config属性
从图2-18中可以看出,按钮控件的State Config属性有4个选项,这4个选项表示的含义如下所示。
- Default:这是按钮默认的状态。
- Highlighted:当用户触碰该按钮时,该按钮显示高亮状态。
- Selected:表示按钮被选中的状态。
- Disabled:表示按钮被禁用后的状态。
针对UIButton属性检查器面板中的设置,UIButton类也定义了相应的属性,接下来,通过一张表来列举UIButton的常见属性,见表2-6。
表2-6 UIButton类的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic,readonly) UIButtonType buttonType; |
用于设置按钮控件的类型 |
@property(nonatomic,readonly,retain) UILabel *titleLabel; |
用于设置按钮控件显示的文本 |
@property(nonatomic,readonly,retain) UIImageView*imageView; |
用于显示按钮控件的图片 |
除此之外,UIButton类还提供了许多可以设置UIButton外观的方法,这些方法都比较常用。表2-7列举了UIButton提供的常见方法。
表2-7 UIButton类的常见方法
方法声明 |
功能描述 |
---|---|
- (void)setTitle:(NSString *)title forState:(UIControlState)state; |
用于设置按钮的文本和状态 |
- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state; |
用于设置按钮的标题颜色和状态 |
- (void)setBackgroundImage:(UIImage *)image forState:(UIControlState)state; |
用于设置按钮的背景图片和状态 |
- (void)setImage:(UIImage *)image forState:(UIControlState)state; |
用于设置按钮的图片和状态 |
表2-7列举的方法中都需要指定一个forState参数,该参数是一个UIControlState整数值,用于接收UIControlStateNormal、UIControlStateHighlighted、UIControlStateDisabled、UIControlStateSelected状态的值,这4个状态所代表的值刚好是图2-18所列出的4种状态。
2.3.2 实战演练——使用按钮移动、旋转、缩放图片
UIButton作为iOS开发中最常用的控件之一,它主要用于响应用户在界面中触发的事件。为了帮助大家熟练掌握UIButton的使用,接下来,带领大家使用按钮控件来控制图片的移动、旋转和缩放,具体步骤如下。
1.创建工程,设计界面
(1)新建一个Single View Application应用,名称为03_UIButton,然后在Main.storyboard界面中添加1个UIImageView控件和8个UIButton控件,其中,UIImageView控件用于显示要操作的图片,UIButton控件用于控制图片的上下左右移动、左右旋转和放大缩小。
(2)将提前准备好的图片放到Supporting Files文件中,并为UIImageView控件和UIButton控件设置背景图片。设计好的界面如图2-19所示。
图2-19 按钮操作图片的界面
在图2-19中,上移、下移、左移、右移的操作分别和图中箭头的方向对应,左旋、右旋和放大、缩小也一样。需要注意的是,为了区分移动、旋转、缩放的不同按钮,我们需要为每个按钮设置一个Tag属性,这里,我们为上移、下移、左移、右移、左旋转、右旋转、放大、缩小设置的Tag属性为1、2、3、4、5、6、7、8。
2.创建控件对象的关联
(1)单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,为具备移动操作的4个UIButton控件添加同一个单击事件,命名为run,如图2-20所示。
图2-20 创建移动按钮对象的关联
(2)同样的方式,为旋转按钮、缩放按钮添加相应的事件,分别命名为rotate和scale,为UIImageView添加属性,命名为img,添加完成后的界面如图2-21所示。
图2-21 完成控件对象关联后的界面
从图2-21中可以看出,完成控件对象的关联后,在ViewController.m文件中会自动生成控件对象对应属性和方法的声明。
3. 通过代码实现图片的移动、旋转、缩放功能
完成控件对象的关联后,就可以通过代码实现按钮移动、旋转、缩放图片的功能了。进入ViewController.m文件,通过代码对不同的方法进行实现,代码如例2-3所示。
【例2-3】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 // 声明一个表示图片的属性img
4 @property (weak, nonatomic) IBOutlet UIImageView *img;
5 - (IBAction)scale:(id)sender; // 声明缩放图片的方法
6 - (IBAction)rotate:(id)sender;// 声明旋转图片的方法
7 - (IBAction)run:(id)sender; // 声明移动图片的方法
8 @end
9 @implementation ViewController
10 - (void)viewDidLoad {
11 [super viewDidLoad];
12 }
13 // 用于缩放图片的方法
14 - (IBAction)scale:(id)sender {
15 // 获取图片的transform属性
16 CGAffineTransformt=self.img.transform;
17 // 获取图片的tag属性
18 NSInteger tag = [sender tag];
19 if (tag==7) {
20 CGAffineTransform temtransform =CGAffineTransformScale(t, 1.2,1.2);
21 self.img.transform= temtransform;
22 }else{
23 CGAffineTransform temtransform=CGAffineTransformScale(t, 0.8,0.8);
24 self.img.transform=temtransform;
25 }
26 }
27 // 用于旋转图片的方法
28 - (IBAction)rotate:(id)sender {
29 // 获取图片的transform属性
30 CGAffineTransform temtransform=self.img.transform;
31 // 获取按钮的tag属性
32 NSInteger rotatetag=[sender tag];
33 if (rotatetag==5) {
34 self.img.transform=CGAffineTransformRotate(temtransform,M_PI_4 * -1);
35 }else{
36 self.img.transform = CGAffineTransformRotate(temtransform,M_PI_4 * 1);
37 }
38 }
39 // 用于移动图片的方法
40 - (IBAction)run:(id)sender {
41 // 获取图片的frame属性
42 CGRect tmpframe = self.img.frame;
43 // 获取按钮的tag属性
44 NSInteger runtag=[sender tag];
45 switch (runtag) {
46 case 1: // 向上移动
47 tmpframe.origin.y-=10;
48 self.img.frame=tmpframe;
49 break;
50 case 2: // 向下移动
51 tmpframe.origin.y+=10;
52 self.img.frame=tmpframe;
53 break;
54 case 3: // 向左移动
55 tmpframe.origin.x-=10;
56 self.img.frame=tmpframe;
57 break;
58 case 4: // 向右移动
59 tmpframe.origin.x+=10;
60 self.img.frame=tmpframe;
61 break;
62 default:
63 break;
64 }
65 }
66 @end
在例2-3中,使用按钮对图片进行各种操作时,首先都是获取图片对象的相关属性,然后根据图片对象的Tag属性,执行不同的动作。以run方法为例,该方法用于实现图片的移动操作,第41~44行代码分别获取了图片的frame和按钮的tag属性,第45~65行代码使用switch语句,根据按钮的tag属性,判断按钮移动的方向,并对frame属性的坐标位置进行修改,从而实现图片的移动。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,单击不同的按钮操作图片,发现图片可以实现移动、旋转和缩放了。使用按钮对图片进行移动、旋转、缩放后的部分图片如图2-22所示。
图2-22 使用按钮移动、旋转、缩放图片
2.4 文本框控件和文本控件
在iOS开发中,经常需要一些显示文字的控件,虽然UILabel标签可以实现文字的展示,但它不能和用户进行交互,为此,UIKit框架提供了UITextField和UITextView文本控件,接下来,本节将针对这两个控件进行详细讲解。
2.4.1 文本框控件(UITextField)
在iOS开发中,文本框控件使用UITextField来表示,它和UIButton控件一样,都直接继承自UIControl控件,并且可以和用户进行交互。为了帮助大家更好地理解什么是文本框控件,接下来,通过新浪微博登录的界面来展示UITextField的使用场景,如图2-23所示。
图2-23 新浪微博登录界面
图2-23所示的是一个新浪微博的登录界面,该界面中的用户名和密码输入框都是使用文本框控件UITextField控件实现的。由此可见,UITextField控件不仅可以显示,同时可供用户输入或者编辑文本。
要想在程序中使用UITextField,首先得学会创建UITextField。同样从对象库中找到Text Field,将其拖曳到Main.storyboard编辑界面中,这样就轻而易举地创建了一个文本输入框。选中Text Field控件,Xcode右侧出现了UITextField的属性检查器面板,该面板用于设置UITextField的相关属性,如图2-24所示。
图2-24 UITextField的属性检测器面板
图2-24所示的是UITextField所支持的一些属性,通过设置这些属性,可以使文本框的状态发生相应变化。接下来,针对Clear Button属性和Keyboard Type属性进行详细讲解。
1. Clear Button
Clear Button用于控制何时显示清除按钮,选中文本框控件,单击该属性的下拉列表,结果如图2-25所示。
图2-25 Clear Button属性支持的选项
图2-25所示的是UITextField控件的Clear Button属性选项,这些选项所代表的含义如下所示。
- Never appears:从不显示清除按钮。
- Appears while editing:当编辑内容时显示清除按钮。
- Appears unless editing:除了编辑之外,都会显示清除按钮。
- Is always visible:清除按钮一直可见。
2 .Keyboard Type
Keyboard Type用于设置文本框关联的键盘类型,选中文本框控件,单击该属性的下拉列表,结果如图2-26所示。
图2-26 Keyboard Type属性支持的选项
图2-26所示的是Keyboard Type属性所支持的选项,这些选项所代表的含义如下所示。
- Default:显示默认的虚拟键盘。
- ASCII Capable:显示英文字母键盘。
- Numbers and Punctuation:显示数字和标点符号键盘。
- Number Pad:显示数字键盘。
- Phone Pad:显示电话拨号键盘。
- E-mail Address:显示输入E-mail地址的虚拟键盘。
- Decimal Pad:显示可输入数字和小数点的虚拟键盘。
针对UITextField属性检查器面板设置的属性,UITextField类也定义了与之对应的属性,接下来通过一张表来列举UITextField的常见属性,见表2-8。
表2-8 UITextField的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic)UIControlContentVerticalAlignment contentVerticalAlignment; |
设置文本框内文本的垂直对齐方式 |
@property(nonatomic,copy)NSString *placeholder; |
设置文本框未输入文本时的提示信息 |
@property(nonatomic)UITextBorderStyle borderStyle; |
设置文本框边框的样式 |
@property(nonatomic) UITextFieldViewModeclearButtonMode; |
设置文本框是否显示清除按钮 |
@property(nonatomic,getter=isSecureTextEntry) BOOL secureTextEntry; |
设置文本框输入的字符是否密文显示 |
@property(nonatomic) UITextAutocorrectionType autocorrectionType; |
设置是否自动更正文本框内的文本内容 |
@property(nonatomic)BOOL clearsOnBeginEditing; |
再次编辑时是否清空之前的文本内容 |
@property(nonatomic)BOOL adjustsFontSizeToFitWidth; |
设置文本内容是否适应文本框窗口大小 |
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType; |
设置文本框内文本内容首字母是否为大写 |
@property(nonatomic,readonly,getter=isEditing) BOOL editing; |
设置文本框内文本内容是否允许编辑 |
@property(nonatomic,assign) id<UITextFieldDelegate> delegate; |
设置代理 |
@property(nonatomic) UIKeyboardType keyboardType; |
设置文本框关联的键盘类型 |
@property(nonatomic) UIKeyboardAppearance keyboardAppearance; |
设置文本框关联键盘的外观样式 |
@property(nonatomic) UIReturnKeyType returnKeyType; |
设置文本框关联键盘的回车键类型 |
@property(nonatomic,retain) UIView *leftView; |
设置文本框左侧视图 |
@property(nonatomic) UITextFieldViewMode leftViewMode; |
设置文本框左视图的显示方式 |
@property(nonatomic,retain) UIView *rightView; |
设置文本框右侧视图 |
@property(nonatomic) UITextFieldViewMode rightViewMode; |
设置文本框右视图的显示方式 |
表2-8列举出了UITextField的常见属性,其中delegate为代理属性,如果一个对象要想监听文本框的动态,如限制文本框输入内容的个数,该对象可以成为文本框的代理来实现监听,但是前提是要遵守UITextFieldDelegate协议,该协议的定义方式如下所示。
@protocol UITextFieldDelegate <NSObject>
@optional
// 文本框是否可以进入编辑模式(是否可进入输入状态)
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField;
// 文本框进入编辑模式
- (void)textFieldDidBeginEditing:(UITextField *)textField;
// 是否退出编辑模式(是否可结束输入状态)
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField;
//退出编辑模式(结束输入状态)
- (void)textFieldDidEndEditing:(UITextField *)textField;
// 当输入任何字符时,代理调用该方法
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:
(NSRange)range replacementString:(NSString *)string;
// 是否可以单击清除按钮
- (BOOL)textFieldShouldClear:(UITextField *)textField;
// 单击键盘上的return按钮时调用
- (BOOL)textFieldShouldReturn:(UITextField *)textField;
@end
从上述代码中可以看出,UITextFieldDelegate协议中定义了许多供代理监听的方法,这些方法会在文本框的不同状态下被调用。例如,textFieldShouldReturn方法是单击键盘上的return键按钮时调用的方法,该方法可以实现单击return键后所执行的行为。
2.4.2 实战演练——用户登录“传智播客”
UITextField作为用户输入文本的控件之一,它经常会运用在一些用户的登录界面,例如,新浪微博的登录界面,用户名和密码的文本输入框就是使用UITextField实现的。为了帮助大家更好地学习UITextField的使用,接下来,带领大家开发一个用户登录传智播客的案例,具体步骤如下。
1.创建工程,设计界面
(1)新建一个Single View Application应用,名称为04_UITextField,然后在Main.storyboard界面中添加一个UIImageView控件、两个UITextField控件和一个UIButton控件,其中,UIImageView控件用于显示Logo图片,UITextField控件用于输入用户登录的用户名和密码,UIButton控件用于用户的登录。
(2)将提前准备好的图片放到Supporting Files文件中,并为UIImageView控件和UIButton控件设置背景图片。为了确保密码的安全,通常情况下,我们都会将密码设置为密文显示,即用于输入密码的UITextField控件中的Secure Text Entry属性勾选上。设计好的界面如图2-27所示。
图2-27 用户登录的界面
2.创建控件对象的关联
(1)单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,为UITextField控件添加用于表示用户名和密码的属性,分别命名username和password,创建好的界面如图2-28所示。
图2-28 为UITextField控件添加属性
(2)同样的方式,为登录按钮添加一个事件,命名为login,添加完成后的界面如图2-29所示。
图2-29 为UIButton控件添加用于单击的方法
3. 通过代码实现用户登录的功能
完成控件对象的关联后,就可以通过代码实现用户登录的功能了。为了演示用户登录的功能,我们假设有一个用户名为itcast,密码为12345的用户。使用代码实现用户登录功能的代码如例2-4所示。
【例2-4】viewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 -(IBAction)login:(id)sender;// 用于声明UIButton按钮的单击事件
4 // 用于声明两个UITextField控件所对应的属性
5 @property (weak, nonatomic) IBOutlet UITextField *password;
6 @property (weak, nonatomic) IBOutlet UITextField *username;
7 @end
8 @implementation ViewController
9 - (void)viewDidLoad {
10 [superviewDidLoad];
11 }
12 -(IBAction)login:(id)sender {
13 // 获取用户名
14 NSString *username=self.username.text;
15 // 获取密码
16 NSString *password=self.password.text;
17 // 判断用户名和密码是否正确
18 if ([username isEqualToString:@""]||[password isEqualToString:@""]) {
19 [self showMessage:@"用户名或密码不能为空"];
20 }elseif(![password isEqualToString:@"12345"]||
21 ![username isEqualToString:@"itcast"]){
22 [self showMessage:@"用户名或密码错误"];
23 }elseif([username isEqualToString:@"itcast"]&&
24 [password isEqualToString:@"12345"]){
25 [self showMessage:@"登录成功"];
26 }
27 }
28 // 提示信息的方法
29 -(void)showMessage:(NSString *) message{
30 UIAlertView *alert=[[UIAlertView alloc] initWithTitle:nil message:
31 message delegate:nil cancelButtonTitle:@"确定"
32 otherButtonTitles:nil,nil];
33 [alert show];
34 }
35 @end
在例2-4中,第18~26行代码用于对用户名和密码进行判断,并且调用了showMessage方法显示登录的结果,showMessage方法是程序第29~34行代码定义的一个方法,该方法首先创建了一个UIAlertView对象,并在创建该对象时指定了警告框的标题、消息内容及警告框包含的按钮信息,然后调用show方法,将创建的UIAlertView对象显示出来。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,在文本框中输入用户名和密码,单击登录按钮,这时,不管用户名和密码输入的是否正确,都会弹出一个对应的提示框,效果如图2-30所示。
图2-30 用户登录的运行结果
2.4.3 多行文本控件(UITextView)
在iOS应用中,经常需要输入多行文本,这时,需要使用UITextView控件实现。与UITextField控件相比,UITextView继承自UIScrollView:UIView类,它不仅可以输入并显示文本,而且可以在固定的区域展示足够多的文本,并且这些文本内容可以换行显示。为了帮助大家更好地理解什么是UITextView,接下来,通过一张发表微博的图片来展示UITextView的应用场景,如图2-31所示。
图2-31 UITextView的使用场景
图2-31所示是发送微博的界面,其中,发表微博内容的区域是一个可以滚动显示的文本框,它是由一个多行文本控件实现的。通常情况下,多行文本控件也称为文本视图。
同样,在学习UITextView控件之前,先来看一下UITextView控件所支持的属性。从对象库中将Text View直接拖曳到Main.storyboard编辑界面并选中,在Xcode右侧会出现了UITextView的属性检查器面板,如图2-32所示。
图2-32 UITextView的属性检测器面板
图2-32所示标识出了UITextView常见属性的设置,这些属性所代表的含义比较简单,大家可以通过修改属性的设置,体会这些属性对UITextView所起的作用。
除了可以在属性检查器面板中设置属性外,还可以使用UITextView类提供的属性来进行设置,接下来,通过一张表来列举UITextView的常见属性,具体如表2-9所示。
表2-9 UITextView的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic,assign) id<UITextViewDelegate> delegate; |
设置代理 |
@property(nonatomic,getter=isEditable) BOOL editable; |
设置文本视图是否可编辑 |
@property(nonatomic,getter=isSelectable) BOOL selectable; |
设置文本视图是否可选择 |
@property(nonatomic) BOOL clearsOnInsertion; |
设置文本视图输入时是否清除之前的文本 |
@property(nonatomic,copy) NSAttributedString *attributedText; |
设置文本视图默认插入的文字内容 |
@property (readwrite, retain) UIView *inputView; |
设置底部弹出的视图 |
@property (readwrite, retain) UIView *inputAccessory View; |
设置底部弹出视图上方的辅助视图 |
@property(nonatomic) UIViewAutoresizing autoresizingMask; |
设置文本视图自动适应高度 |
表2-9列举了UITextView的常见属性,其中delegate为代理属性,文本视图的事件交由代理对象处理,实现对文本视图的监听,但是前提要遵守UITextViewDelegate协议,该协议的定义方式如下所示。
@protocol UITextViewDelegate <NSObject, UIScrollViewDelegate>
@optional
// 用户将要开始编辑UITextView的内容时会激发该方法
- (BOOL)textViewShouldBeginEditing:(UITextView *)textView;
// 用户开始编辑该UITextView的内容时会激发该方法
- (void)textViewDidBeginEditing:(UITextView *)textView;
// 用户将要结束编辑该UITextView的内容时会激发该方法
- (BOOL)textViewShouldEndEditing:(UITextView *)textView;
//用户结束编辑该UITextView的内容时会激发该方法
- (void)textViewDidEndEditing:(UITextView *)textView;
// 该UITextView内指定范围内的文本内容将要被替换时激发该方法
- (BOOL)textView:(UITextView *)textView
shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
// 该UITextView中包含的文本内容发生改变时会激发该方法
- (void)textViewDidChange:(UITextView *)textView;
// 用户选中该UITextView内某些文本时会激发该方法
- (void)textViewDidChangeSelection:(UITextView *)textView;
@end
从上述代码中可以看出,UITextViewDelegate协议中定义了很多方法,这些方法会在不同的状态下被激发。例如,textView:shouldChangeTextInRange:方法是替换多行文本控件中指定文本时会触发的方法,该方法可以实现把回车键当作退出键盘的响应键。
2.5 开关控件(UISwitch)
2.5.1 开关控件概述
在某些iOS应用中,经常会看到一些类似传统物理开关的控件,例如,手电筒的开关、Setting选项中的定位开关等,这些开关都是使用开关控件UISwitch实现的,为了大家更好地理解什么是开关控件,接下来,通过一张图来描述开关控件的应用场景,如图2-33所示。
图2-33 Setting选项中的Safari选项
图2-33所示的是一个Setting选项中的Safari选项界面,该界面中包含了很多UISwitch控件,并且这些控件都只有“开/关”两种状态。
UISwitch控件继承自UIControl类,它是一个可以与用户进行交互的控件。同样,在学习UISwitch控件之前,先来看一下UISwitch所支持的属性。将对象库中的UISwitch控件拖曳到Main.storyboard编辑界面并选中,Xcode右侧会出现UISwitch的属性检查器面板,如图2-34所示。
图2-34 UISwitch控件的属性检查器面板
从图2-34中可以看出,UISwitch控件属性检查器面板中的属性比较少,其中,State属性用于切换开关控件的“开/关”状态,应用程序可以通过属性on或方法isOn来检测当前的状态。
2.5.2 实战演练——使用开关控制“灯泡”
在iPhone或iPad设备上,经常会看到类似于手电筒这种应用,它的界面比较简单,只有一个开关控制手电筒的打开关闭状态,出于这种设计思路,接下来,我们使用开关控件UISwitch来控制“灯泡”,当用户打开UISwitch时,应用程序界面的灯泡图片是点亮的,当用户关闭UISwitch控件时,灯泡的图片是熄灭的,具体步骤如下。
1.创建工程,设计界面
(1)新建一个Single View Application应用,名称为05_UISwitch,然后在Main.storyboard界面中添加一个UIImageView控件和一个UISwitch控件,其中,UIImageView控件用于显示灯泡的图片,UISwitch控件用于显示开关。
(2)将提前准备好的图片放到Supporting Files文件中,并将UISwitch控件的初始状态设置为打开,UIImageView控件的初始图片是灯泡点亮的状态,设计好的界面如图2-35所示。
图2-35 使用开关控制灯泡的界面
2.创建控件对象的关联
单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,为UIImageView控件添加表示图片对象的属性,命名为img;同理,为UISwitch控件添加切换开关状态的方法,命名为change,添加完成后的界面如图2-36所示。
图2-36 为控件对象添加关联
3. 通过代码实现灯泡开关的控制
完成控件对象的关联后,就可以通过代码实现灯泡开关的控制了。进入ViewController.m文件,在change方法中,首先判断灯泡切换时的状态,然后根据灯泡开关的状态对灯泡的图片进行切换。使用开关控制灯泡的代码如例2-5所示。
【例2-5】viewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 -(IBAction)change:(id)sender;
4 @property (weak, nonatomic) IBOutlet UIImageView *img;
5 @end
6 @implementation ViewController
7 - (void)viewDidLoad {
8 [super viewDidLoad];
9 }
10 - (IBAction)change:(id)sender {
11 if(![sender isOn]){
12 // 获取文件的名称
13 NSString *filename=[NSString stringWithFormat:@"img_01.jpg"];
14 // 加载图片
15 UIImage *image=[UIImage imageNamed:filename];
16 // 更换图片
17 self.img.image=image;
18 }else {
19 // 获取文件的名称
20 NSString *filename=[NSStringstringWithFormat:@"img_02.jpg"];
21 // 加载图片
22 UIImage *image=[UIImageimageNamed:filename];
23 // 更换图片
24 self.img.image=image;
25 }
26 }
27 @end
在例2-5中,change方法是控制灯泡开关的核心代码,当程序通过调用isOn方法后,如果判断的结果不是YES,则说明控制灯泡的开关是关闭状态,这时,将当前界面的图片使用img_02替换。同理,如果开关是打开状态,则将当前页面的图片替换为img_01。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,单击开关按钮,发现使用开关可以控制灯泡,效果如图2-37所示。
2.6 滑块控件(UISlider)
2.6.1 滑块控件概述
在播放器界面,经常可以看到类似于进度条的控件,例如,音量的控制、播放进度的控制。在iOS开发中,这种使用滑块来改变数值的控件称为滑块控件。滑块控件使用UISlider表示,它继承自UIControl,是一个可以与用户进行交互的控件。接下来,通过一张音乐播放的图片来展示UISlider的应用场景,如图2-38所示。
图2-37 使用开关控制灯泡的运行结果
图2-38 音乐播放器界面
图2-38所示的是一个音乐播放器的界面,该播放器中的歌曲播放进度就是一个滑块控件,它可以通过拖动的方式来改变音乐的播放进度。
同学习其他控件一样,从对象库中将滑块控件拖曳到Main.storyboard编辑界面中并选中,在 Xcode右侧查看UISlider的属性检查器面板中的相关属性,如图2-39所示。
图2-39 UISlider的属性检查器面板
图2-39展示了UISlider支持的一些属性,这些属性都可以改变UISlider的状态。同时,针对UISlider属性检查器面板设置的属性,UISlider类也定义了与之对应的属性,接下来通过一张表来列举UISlider的常见属性,见表2-10。
表2-10 UISlider的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic) float value; |
设置或者获取滑块的值 |
@property(nonatomic) float minimumValue; |
设置滑块的最小值,默认为0.0 |
@property(nonatomic) float maximumValue; |
设置滑块的最大值,默认为1.0 |
@property(nonatomic,retain) UIImage *minimumValueImage; |
设置滑块最小值边界的图片 |
@property(nonatomic,retain) UIImage *maximumValueImage; |
设置滑块最大值边界的图片 |
@property(nonatomic,retain) UIColor *minimumTrackTintColor; |
设置小于滑块当前值的轨道颜色,默认为蓝色 |
@property(nonatomic,retain) UIColor *maximumTrackTintColor; |
设置大于滑块当前值的轨道颜色,默认为白色 |
@property(nonatomic,retain) UIColor *thumbTintColor; |
设置当前拖动条的颜色,默认为白色 |
表2-10列举出了UISlider常见的一些属性,它们都可以改变滑块的样式。同时UISlider类还提供了相应的方法,用来定制滑块的外观,接下来通过一张表来列举UISlider的常见方法,见表2-11。
表2-11 UISlider的常见方法
方法声明 |
功能描述 |
---|---|
- (void)setThumbImage:(UIImage *)image forState:(UIControlState)state; |
设置滑块上拖动条的图片 |
- (void)setMinimumTrackImage:(UIImage *)image forState:(UIControlState)state; |
设置滑块已完成进度的轨道图片 |
- (void)setMaximumTrackImage:(UIImage *)image forState:(UIControlState)state; |
设置滑块未完成进度的轨道图片 |
表2-11列举出了UISlider常见的一些方法,这些方法都需要传入UIImage对象,使用图片来改变UISlider的外观。
2.6.2 实战演练——使用滑块控制音量
在大多数应用中,音量的控制都是通过滑块控件实现的。为了帮助大家更好地学习滑块控件UISlider的使用,接下来,我们来模拟实现一个控制音量的功能,具体步骤如下:
1.创建工程,设计界面
(1)新建一个Single View Application应用,名称为06_UISlider,然后在Main.storyboard界面中添加一个UISlider控件、一个UILabel控件和一个UIImageView控件,其中UIImageView用于显示图片,UILabel用于提示用户拖动滑块,UISlider用于根据不同的阶段的值切换图片。
(2)将UISlider控件的Current属性设置为0.4,UILabel控件的text属性设置为“提示:请拖动滑块改变音量”。
(3)将提前准备好的图片资源放到Supporting Files文件中,为UIImageView设置默认显示的图片,设计好的界面如图2-40所示。
图2-40 使用滑块控制音量的界面
2.创建控件对象的关联
单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,选中UIImageView,添加一个表示音量的对象,命名为voiceImageV,同样,为UISlider控件添加一个表示音量控制的滑块对象,命名为slider,添加完成后的界面如图2-41所示。
图2-41 创建控件对象关联后的界面
3. 通过代码实现调节音量的功能
完成控件对象的关联后,就可以通过代码实现调节音量的功能了。进入ViewController.m文件,根据滑块数值的变化适时地切换音量图片,代码如例2-6所示。
【例2-6】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 // 滑块
4 @property (weak, nonatomic) IBOutlet UISlider *slider;
5 // 音量图片
6 @property (weak, nonatomic) IBOutlet UIImageView *voiceImageV;
7 @end
8 @implementation ViewController
9 - (void)viewDidLoad {
10 // 1.为滑块控件UISlider添加监听器
11 [self.slider addTarget:self action:@selector(valueChange:)
12 forControlEvents:UIControlEventValueChanged];
13 }
14 // 滑块数值发生变化时会调用的方法
15 - (void)valueChange:(UISlider *)slider0
16 {
17 // 1.设置图片的数量
18 int count = 4;
19 // 2.获取滑块的值
20 float level = slider0.value;
21 // 3.根据滑块的值适时切换图片
22 if (level>=0 && level<1.0/(count-1)) {
23 self.voiceImageV.image = [UIImage imageNamed:@"voice0.jpg"];
24 }else if (level>=1.0/(count-1) && level<2.0/(count-1)){
25 self.voiceImageV.image = [UIImage imageNamed:@"voice1.jpg"];
26 }else if(level>=2.0/(count-1) && level<1){
27 self.voiceImageV.image = [UIImage imageNamed:@"voice2.jpg"];
28 }else if(level == 1){
29 self.voiceImageV.image = [UIImage imageNamed:@"voice3.jpg"];
30 }
31 }
32 @end
在例2-6中,第11~12行代码为滑块控件添加监听器方法,并设置数值变化的监听方法是valueChange;第15~31行代码则是方法valueChange的具体实现,该方法通过划分UISlider控件,更换不同的表示音量的图片,从而实现了对音量的控制。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功以后,拖动滑块,发现“音量”可以根据滑块的拖动来改变大小。使用滑块控制音量的部分运行结果如图2-42所示。
图2-42 使用滑块实现音量调节
2.7 分段控件(UISegmentControl)
2.7.1 分段控件概述
目前,很多手机App程序都会在界面上设计一栏按钮,这些按钮可以通过切换,在屏幕上展现不同的内容,例如,网易新闻页面频道栏中的头条、娱乐、图片等,在iOS中,这种可以在不同类别信息间进行切换的控件,称为分段控件,接下来,通过一张图片来展示分段控件的应用场景,如图2-43所示。
图2-43 公交换乘的应用
分段控件使用UISegmentControl表示,它继承自UIControl,是一个可活动的控件。同其他控件一样,分段控件也可以通过拖曳的方式创建。将对象库中的Segment Control控件拖曳到Main.storyboard编辑界面中并选中,在Xcode右侧会出现UISegmentControl的属性检查器面板,如图2-44所示。
图2-44 UISegmentControl的属性检查器面板
从图2-44中可以看出,UISegmentControl控件属性检查器面板中的属性比较少,其中,Segments属性的值是一个整数,它用于控制分段控件被分为几段;Segment属性则是一个列表框,它所包含的列表项随着Segments属性设置的值而改变,例如,Segments的属性设为4,那么Segment的列表框将包含4个列表项,并且Segment 0 代表第1个分段,Segment 1代表第2个分段,依次类推。
2.7.2 实战演练——使用分段控件控制“花朵”
分段控件在实际开发中应用是非常广泛的,为了帮助大家更好地掌握分段控件,接下来,我们使用分段控件开发一个控制花朵颜色的案例,该案例中共有3个分段,单击每个分段就会出现不同颜色的花朵,具体步骤如下。
1.创建工程,设计界面
(1)新建一个Single View Application应用,命名07_UISegmentControl,然后在Main.storyboard界面中添加一个UILabel控件、一个UIImageView控件和一个UISegmentControl控件,其中,UILabel控件用于提示用户选择花朵颜色,UIImageView控件用于显示花朵,UISegmentControl控件用于控制花朵的显示。
(2)将提前准备好的图片放到Supporting Files文件中,将UISegmentControl的Segments属性设置为3,Segment属性分别设置为对应的花朵图片,设计好的界面如图2-45所示。
图2-45 使用分段控件控件花朵的界面
2.创建控件对象的关联
单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,为UIImageView控件添加表示图片对象的属性,命名为img;同理,为UISegmentControl控件添加单击按钮的方法,命名为selectchange,添加完成后的界面如图2-46所示。
图2-46 为控件对象创建关联
3. 通过代码实现用户登录的功能
完成控件对象的关联后,就可以通过代码实现花朵的切换了。进入ViewController.m文件,在selectchange方法中,根据分段控件的Segment属性判断单击的是哪个分段,从而控制不同颜色花朵的显示。使用分段控件控制花朵的代码如例2-7所示。
【例2-7】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 - (IBAction)selectchange:(id)sender;// 声明用于切换图片的分段控件
4 @property (weak, nonatomic) IBOutlet UIImageView *img;//声明一个图片控件
5 @end
6 @implementation ViewController
7 - (void)viewDidLoad {
8 [super viewDidLoad];
9 }
10 - (IBAction)selectchange:(id)sender {
11 // 创建要切换的图片对象
12 UIImage *img1=[UIImage imageNamed:@"flower_01.jpg"];
13 UIImage *img2=[UIImage imageNamed:@"flower_02.jpg"];
14 UIImage *img3=[UIImage imageNamed:@"flower_03.jpg"];
15 switch ([sender selectedSegmentIndex]) {
16 case 0:
17 self.img.image=img1;
18 break;
19 case 1:
20 self.img.image=img2;
21 break;
22 case 2:
23 self.img.image=img3;
24 break;
25 default:
26 break;
27 }
28 }
29 @end
在例2-7中,第12~14行代码创建了3个UIImage对象;第15~28行代码通过判断分段控件选中的位置,为UIImageView对象设置图片。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,单击分段控件中的每个分段,发现分段控件可以按照期望控制不同颜色的花朵了。效果如图2-47所示。
图2-47 使用分段控件控制花朵的运行结果
2.8 数据选择控件
2.8.1 日期选择控件(UIDatePicker)
UIDatePicker是一个可以用来选择日期和时间的控件,它继承自UIControl,是一个可以与用户交互的控件。例如,计时器应用中就用到了UIDatePicker控件,如图2-48所示。
图2-48 计时器应用
UIDatePicker控件同样可以通过拖曳的方式创建,从对象库中找到Date Picker控件,并将其拖曳到Main.storyboard编辑界面中,这时,在Xcode右侧看到UIDatePicker属性检查器面板中的相关属性,如图2-49所示。
图2-49 UIDatePicker的属性检查器面板
图2-49所示的是UIDatePicker所支持的一些属性,其中,Mode和Date属性都支持多种选项,下面针对这两种属性进行详细讲解。
1. Mode
Mode属性用于设置UIDatePicker的模式,它包含4种选项,单击Mode属性的下拉列表,如图2-50所示。
图2-50 mode属性的4种选项
从图2-50中可以看出,UIDatePicker控件有4种模式可以选择,这4种模式的相关讲解具体如下:
- Time:该UIDatePicker控件只显示时间,不显示日期。
- Date:该UIDatePicker控件只显示日期,不显示时间。
- Date and Time:该UIDatePicker控件同时选择日期和时间。
- Count Down Timer:该UIDatePicker控件仅显示倒计时器。
2. Date
Date属性用于设置日期选择器的当前时间,它包含两个选项,单击Date属性的下拉列表,如图2-51所示。
图2-51 Date属性的选项
从图2-51中可以看出,Date属性支持两种选项,这两种选项所表示的含义具体如下。
- Current Date:表示系统当前的时间。
- Custom:表示自定义的时间,该时间用户可以任意指定。
针对日期选择控件在属性检查器面板中所支持的属性,UIDatePicker类提供了对应的属性,接下来,通过一张表来描述UIDatePicker类提供的常见属性,见如表2-12。
表2-12 UIDatePicker提供的常见属性
属性声明 |
功能描述 |
---|---|
@property (nonatomic) UIDatePickerMode datePickerMode; |
设置UIDatePicker的模式 |
@property (nonatomic, retain) NSLocale*locale; |
设置UIDatePicker为国际化时间 |
@property (nonatomic, retain) NSDate *date; |
设置UIDatePicker的当前时间 |
@property (nonatomic) NSTimeInterval countDownDuration; |
当UIDatePicker模式为Count Down Timer时,设置剩余时间 |
表2-12中列举了UIDatePicker类的常见属性,其中,countDownDuration属性是专门针对CountDown Timer模式的,它用于获取倒计时的剩余时间。
2.8.2 实战演练——倒计时
在UIDatePicker的属性检查器面板中,如果将Mode属性设置为Count Down Timer,就可以将UIDatePicker控件当作倒计时器使用。接下来,通过一个案例来演示如何使用UIDatePicker控件实现倒计时的功能,具体步骤如下。
1.界面设计
(1)新建一个Single View Application应用,命名08_UIDatePicker,然后在Main.storyboard界面中添加一个UIDatePicker控件、一个UIButton控件,其中,UIDatePicker控件用于显示时间,UIButton控件用于开始计时。为了界面美观,我们在界面中添加几个UIImageView控件,作为倒计时的图标。
(2)将提前准备好的图片放到Supporting Files文件中,将UIDatePicker控件的Mode属性设置为Count Down Timer,Date属性设置Custom,设计好的界面如图2-52所示。
图2-52 倒计时界面
2.创建控件对象的关联
单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,为UIDatePicker控件创建表示日期控件的属性,命名为datepicker,为UIButton控件添加单击事件,命名为click。另外,由于单击按钮后,相当于启动了定时器更新UIDatePicker控件,这时,需要禁用UIButton控件和UIDatePicker控件,并更改按钮前面的图片,因此,需要为UIButton、UIDatePicker和UIImageView分别添加一个属性。添加完成后的界面如图2-53所示。
图2-53 创建控件对象的关联
3. 通过代码实现用户登录的功能
完成控件对象的关联后,就可以通过代码实现倒计时的功能了,进入ViewController.m文件,在click方法中,首先获取倒计时的剩余时间,然后启动定时器定时更新UIDatePicker的时间。ViewController.m文件的代码如例2-8所示。
【例2-8】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 - (IBAction)click:(id)sender;
4 @property (weak, nonatomic) IBOutlet UIButton *btn_start;
5 @property (weak, nonatomic) IBOutlet UIImageView *img;
6 @property (weak, nonatomic) IBOutlet UIDatePicker *datepicker;
7 @end
8 @implementation ViewController
9 NSTimer *timer;
10 NSTimeInterval lefttime;
11 - (void)viewDidLoad {
12 [super viewDidLoad];
13 }
14 - (IBAction)click:(id)sender {
15 // 获取倒计时器的剩余时间
16 lefttime=self.datepicker.countDownDuration;
17 // 禁用UIDatePicker和UIButton控件
18 self.datepicker.enabled=NO;
19 [sender setEnabled:NO];
20 // 替换图片
21 UIImage *image=[UIImage imageNamed:@"img_03"];
22 self.img.image=image;
23 // 初始化一个字符串,用于提示用户开始倒计时
24 NSString *message=[NSString stringWithFormat:@"您还剩下【%f】秒", lefttime];
25 // 创建一个警告框,提示开始倒计时
26 UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"开始倒计时"
27 message:message delegate:nil cancelButtonTitle:@"确定"
28 otherButtonTitles:nil, nil];
29 // 显示UIAlertView控件
30 [alert show];
31 // 启用计时器,控制每隔60秒执行一次tickDown方法
32 timer=[NSTimer scheduledTimerWithTimeInterval:60 target:self
33 selector:@selector(tickDown)userInfo:nil repeats:YES];
34 }
35 -(void) tickDown{
36 lefttime-=60;
37 self.datepicker.countDownDuration=lefttime;
38 if (lefttime<=0) {
39 // 取消定时器
40 [timer invalidate];
41 // 启用UIDatePicker和UIButton控件,
42 self.datepicker.enabled=YES;
43 self.btn_start.enabled=YES;
44 // 替换图片
45 UIImage *image=[UIImage imageNamed:@"img_02"];
46 self.img.image=image;
47 }
48 }
49 @end
在例2-8中,click方法是倒计时功能的具体实现,在该方法中,第32~34行代码启动了一个定时器,用于控制每隔60秒执行一次tickDown方法,而tickDown方法每执行一次,程序就会将剩余时间减少60秒,直到剩余时间小于等于0为止。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,单击开始计时的按钮,会弹出一个提示框,提示剩余时间,单击确定按钮后,就可以看到该倒计时器每隔60秒跳动一次,剩余时间减少1分钟。以开始时间为1小时4分钟为例,倒计时的效果如图2-54所示。
图2-54 倒计时效果
2.8.3 选择控件(UIPickerView)
除了日期选择控件外,还有一种选择控件是UIPickerView,它直接继承于UIView,是一个既能生成单列选择器,也能生成多列选择器,又能自定义外观的静态控件。为了让大家更好地认识什么是选择控件,接下来通过一张图片展示UIPickerView的使用场景,如图2-55所示。
图2-55 UIPickerView的使用场景
图2-55所示的是一个水果机应用的界面,该应用界面中的水果选择控件是使用UIPickerView实现的。
同样,将Picker View从对象库中拖曳到Main.storyboard编辑界面并选中, Xcode右侧出现了UIPickerView的属性检查器面板,该面板用于设置UIPickerView的相关属性,如图2-56所示。
图2-56 UIPickerView的属性检查器面板
图2-56所示了UIPickerView的Behavior属性,该属性用于显示选中行的标记,一般以高亮背景作为选中标记。同时,UIPickerView类也提供了相应的属性,接下来通过一张表来列举UIPickerView的常见属性,见表2-13。
表2-13 UIPickerView的常见属性
方法声明 |
功能描述 |
---|---|
@property(nonatomic,assign) id<UIPickerViewDataSource> dataSource; |
设置数据源 |
@property(nonatomic,assign) id <UIPickerViewDelegate> delegate; |
设置代理 |
@property(nonatomic) BOOL showsSelectionIndicator; |
设置是否显示UIPickerView的选中标记 |
@property(nonatomic,readonly) NSInteger numberOfComponents; |
获取UIPickerView指定列中包含的列表项的数量,该属性权限为只读 |
表2-13列举了UIPickerView的一些常见属性,其中dataSource和delegate这两个属性非常重要,接下来,针对它们进行详细讲解。
1. dataSource
dataSource代表数据源,该属性用于指定UIPickerView的数据源,它知道该控件应该展示的列数和行数,但是前提要遵守UIPickerViewDataSource协议,该协议的定义方式如下所示。
@protocol UIPickerViewDataSource<NSObject>// 数据源协议
@required
// 返回选择器总共有多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
// 返回选择器每列总共有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView
numberOfRowsInComponent:(NSInteger)component;
@end
从上述可以看出,UIPickerViewDataSource协议定义了两个方法,这些方法是使用@required关键字修饰的必须要实现的方法,通过委托代理的方式限制数据信息展示的样式,如行数和列数,因此我们又称之为数据源协议。
2. delegate
delegate表示代理,该属性用于设定UIPickerView的代理,实现代理对UIPickerView的监听,但是前提要遵守UIPickerViewDelegate协议,该协议的定义方式如下所示。
@protocol UIPickerViewDelegate<NSObject>// 代理协议
@optional
// 返回第component列每一行的宽度
- (CGFloat)pickerView:(UIPickerView *)pickerView
widthForComponent:(NSInteger)component;
// 返回第component列每一行的高度
- (CGFloat)pickerView:(UIPickerView *)pickerView
rowHeightForComponent:(NSInteger)component;
// 设置选择器每行显示的文本内容
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component;
// 设置选择器每行显示的视图内容
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row
forComponent:(NSInteger)component reusingView:(UIView *)view;
//单击选择器某列某行时,就会调用这个方法
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
inComponent:(NSInteger)component;
@end
从上述代码可以看出,UIPickerViewDelegate协议定义了一些方法,这些方法都可以展示选择器每行每列的信息,且方法是可选择实现的。例如,pickerView:titleForRow:forComponent方法根据相应的数据,展示选择器每行对应的文本内容。
除了协议方法之外,UIPickerView类也定义了一些常见的方法,接下来通过一张表列举UIPickerView的常见方法,见表2-14。
表2-14 UIPickerView的常见方法
方法声明 |
功能描述 |
---|---|
-(NSInteger)numberOfRowsInComponent:(NSInteger)component; |
返回第component列有多少行 |
- (CGSize)rowSizeForComponent:(NSInteger)component; |
返回第component列中一行的尺寸 |
- (UIView *)viewForRow:(NSInteger)row forComponent:(NSInteger)component; |
设置选择器某列某行的视图内容 |
- (void)reloadAllComponents; |
刷新所有列的数据 |
- (void)reloadComponent:(NSInteger)component; |
刷新某一列的数据 |
- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated; |
设置是否动画选中某列某行 |
- (NSInteger)selectedRowInComponent:(NSInteger)component; |
返回选中的是第component列的第几行 |
表2-14列举了UIPickerView类一些常见的方法,这些方法配合使用,可以协调展示不同样式的选择器。
2.8.4 实战演练——点菜系统
随着社会的发展,人们对生活质量的要求不断提高,也越来越注重于饮食。去酒店吃饭成了人们生活中的一部分。为了提高酒店的服务质量,开发一套完善的酒店点菜系统是必要的。接下来,带领大家使用UIPickerView开发一个“点餐系统”,具体步骤如下。
1.创建工程,设计界面
(1)新建一个Single View Application应用,名称为09_UIPickerView,然后在Main.storyboard界面中添加1个UIPickerView、1个UIView、3个UIButton和9个UILabel,其中UIPickerView用于滚动选中菜系内容,UILabel用于显示选择器选中的菜系,UIButton用于随机选中菜系。
(2)设置UIView的背景颜色,并将用于保存菜系数据的foods.plist文件放到Supporting Files文件中,设计好的界面如图2-57所示。
图2-57 点菜系统界面
2.创建控件对象的关联
(1)单击Main.storyboard左下角的图标,打开文档大纲区,选中UIPickerView右击,弹出一个黑框列表,从该列表dataSource选项后的空圆圈拖线到文档大纲区中的控制器文件,设置UIPickerView的dataSource为控制器,如图2-58所示。
图2-58 设置UIPickerView的dataSource为控制器
(2)同样的方式,设置UIPickerView的delegate为控制器,如图2-59所示。
图2-59 设置UIPickerView的delegate为控制器
(3)使用控件和代码关联的方式,为UIButton添加单击事件,添加UILabel和UIPickerView属性,添加完成后的界面如图2-60所示。
图2-60 完成控件对象关联的界面
3. 通过代码实现点菜的功能
完成控件对象的关联后,就可以通过代码实现点菜的功能了。进入ViewController.m文件,遵守数据源和代理协议,将数据信息展示到UIPickerView控件中,并将选中的行内容显示到对应UILabel上,代码如例2-9所示。
【例2-9】ViewController.m
1 #import "ViewController.h"
2 // 遵守数据源和代理协议
3 @interface ViewController ()<UIPickerViewDataSource, UIPickerViewDelegate>
4 @property (nonatomic, strong)NSArray *foods; // 保存plist文件中所有的food
5 @property (weak, nonatomic) IBOutlet UILabel *fruitLabel;
6 @property (weak, nonatomic) IBOutlet UILabel *mainLabel;
7 @property (weak, nonatomic) IBOutlet UILabel *drinkLabel;
8 @property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
9 - (IBAction)random; // 生成随机套餐方法
10 - (IBAction)certain; // 确定方法
11 - (IBAction)cancel; // 取消方法
12 @end
13 @implementation ViewController
14 // 懒加载数组
15 - (NSArray *)foods
16 {
17 if (_foods == nil) {
18 _foods = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle]
19 pathForResource:@"foods" ofType:@"plist"]];
20 }
21 return _foods;
22 }
23 - (void)viewDidLoad {
24 [super viewDidLoad];
25 for (int i = 0; i < 3; i++) { // 选中每一列的第一行
26 [self pickerView:nil didSelectRow:0 inComponent:i];
27 }
28 }
29 #pragma mark - UIPickerViewDataSource
30 // 总共有多少列
31 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView
32 {
33 return self.foods.count;
34 }
35 // 每列总共有多少行
36 - (NSInteger)pickerView:(UIPickerView *)pickerView
37 numberOfRowsInComponent:(NSInteger)component
38 {
39 NSArray *subFood = self.foods[component];
40 return subFood.count;
41 }
42 #pragma mark - UIPickerViewDelegate
43 // 第component列的第row行显示文字内容
44 - (NSString *)pickerView:(UIPickerView *)pickerView
45 titleForRow:(NSInteger)row forComponent:(NSInteger)component
46 {
47 return self.foods[component][row];
48 }
49 // 选中了第component列的第row行
50 - (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row
51 inComponent:(NSInteger)component
52 {
53 // 将选中行的文本显示到对应的Label
54 if (component == 0) { // 水果Label
55 self.fruitLabel.text = self.foods[component][row];
56 }else if (component == 1){ // 主菜Label
57 self.mainLabel.text = self.foods[component][row];
58 }else if(component == 2){ // 饮料Label
59 self.drinkLabel.text = self.foods[component][row];
60 }
61 }
62 // 设置行高
63 - (CGFloat)pickerView:(UIPickerView *)pickerView
64 rowHeightForComponent:(NSInteger)component
65 {
66 return 35;
67 }
68 // 随机选中一份套餐
69 - (IBAction)random {
70 for (int component = 0; component < self.foods.count; component++) {
71 // 1.第component列数组的总长度
72 int count = [self.foods[component] count];
73 // 2.旧的行号
74 int oldRow = [self.pickerView selectedRowInComponent:component];
75 // 3.第几行(默认新的行号跟旧的行号一样)
76 int row = oldRow;
77 // 4.保证行数跟上一次不一样
78 while (row == oldRow) {
79 row = arc4random()%count;
80 }
81 // 5.让pickerView主动选中第compoent列的第row行
82 [self.pickerView selectRow:row inComponent:component animated:YES];
83 // 6.设置label的文字
84 [self pickerView:nil didSelectRow:row inComponent:component];
85 }
86 }
87 // 单击确定
88 - (IBAction)certain {
89 // 提示用户点餐成功
90 UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"点餐成功"
91 message:@"请稍后。。" delegate:nil cancelButtonTitle:@"好的"
92 otherButtonTitles:nil, nil];
93 [alert show];
94 }
95 // 单击取消
96 - (IBAction)cancel {
97 // 直接回到初始化时候
98 for (int i = 0; i < 3; i++) {
99 [self pickerView:nil didSelectRow:0 inComponent:i];
100 [self.pickerView selectRow:0 inComponent:i animated:YES];
101 }
102 }
103 @end
在例2-9中,第3行代码遵守了数据源和代理协议;第4行代码定义了一个数组,用于保存plist文件中所有的foods数据;第14~22行代码使用懒加载的方法初始化数组;第29~67行代码分别为数据源和代理协议中定义的一些方法,用于展示数组中的数据;第69~102行代码分别为单击随机、确定和取消按钮后执行的行为。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,UIPickerView选中的数据成功展示到Label中,单击随机、确定和取消按钮,也都一一对应完成了相应功能。程序运行结果的部分结果如图2-61所示。
图2-61 程序运行部分场景图
多学一招:懒加载
我们知道iOS设备的内存有限。如果程序在启动后就一次性加载应用程序将来会用到的所有资源(如大量数据、图片、音频等),那么就有可能会耗尽iOS设备的内存,就会造成应用程序运行缓慢,或者出现卡顿,甚至于应用程序会发生崩溃。同很多语言一样,iOS中用“懒加载”的方式来加载这些资源,对这些资源进行合理化管理。
懒加载又称之为“延迟加载”,说通俗一点,就是在开发中,程序启动的时候不立刻使用的资源先不加载,当程序运行中需要使用资源的时候再去加载它。懒加载用于get方法,主要有以下好处。
- 效率低,占用内存小。
- 不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强。
- 每个控件的get方法中负责自身的实例化,代码彼此之间的独立性强,耦合度低。
2.9 屏幕滚动控件(UIScrollView)
2.9.1 屏幕滚动控件概述
移动设备的屏幕大小是极其有限的,因此直接展示在用户眼前的内容也是有限的。当屏幕展示的内容较多、超出一个屏幕时,用户可以通过滚动的方式来查看屏幕外的内容。在iOS中,UIScrollView是一个支持滚动的控件,它直接继承自UIView,可以用来展示大量的内容,并且可以通过滚动的方式查看所有的内容。为了让大家更好地理解,接下来通过一张图片来展示UIScrollView的使用场景,如图2-62所示。
图2-62 UIScrollView的使用场景
图2-62所示是一个新闻页面,该页面有很多内容需要展示,因此,该页面中包含了一个UIScrollView控件,用户可以通过滚动的方式来在有限的屏幕中查看更多内容。
UIScrollView控件同其他控件一样,都包含很多属性,同样,从对象库中找到Scroll View,将其拖曳到Main.storyboard编辑界面中,在Xcode右侧会出现UIScrollView的属性检测器面板,该面板可以设置UIScrollView的相关属性,如图2-63所示。
图2-63 UIScrollView的属性检测器面板
针对UIScrollView属性检查器面板设置的属性,UIScrollView类也定义了与之对应的属性,接下来通过一张表来列举UIScrollView的常见属性,见表2-15。
表2-15 UIScrollView的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic) CGPoint contentOffset; |
设置滚动视图的滚动偏移量 |
@property(nonatomic)CGSize contentSize; |
设置滚动视图的滚动范围 |
@property(nonatomic)UIEdgeInsets contentInset; |
设置滚动视图的额外滚动区域 |
@property(nonatomic,assign) id<UIScrollViewDelegate>delegate; |
设置代理 |
@property(nonatomic,getter=isScrollEnabled) BOOL scrollEnabled; |
设置滚动视图是否允许滚动 |
@property(nonatomic,getter=isPagingEnabled) BOOL pagingEnabled; |
设置滚动视图是否开启分页 |
@property(nonatomic) BOOL showsHorizontalScrollIndicator; |
设置滚动视图是否显示水平滚动条 |
@property(nonatomic)BOOL showsVerticalScrollIndicator; |
设置滚动视图是否显示垂直滚动条 |
@property(nonatomic) CGFloat minimumZoomScale; |
设置滚动视图的最小缩放比例 |
@property(nonatomic) CGFloat maximumZoomScale; |
设置滚动视图的最大缩放比例 |
@property(nonatomic) BOOL scrollsToTop; |
设置滚动视图是否滚动到顶部 |
表2-15列举了UIScrollView的常见属性,其中contentOffset、contentSize、contentInset是UIScrollView支持的3个控件显示区域属性, delegate为代理属性,这些属性都比较重要,接下来针对这几个属性进行详细介绍。
1. contentSize
该属性是一个CGSize类型的值,CGSize是一个结构体类型,它包含width、height两个成员变量,代表着该UIScrollView所需要显示内容的完整高度和完整宽度。例如,内容视图为灰色部分,它的大小为320×544,而ScrollView视图的大小只有320×460,由于内容视图超出了ScrollView可显示的大小,因此,需要滚动屏幕来查看内容,如图2-64所示。
图2-64 contentSize属性
2.contentInset
该属性是一个UIEdgeInsets类型的值,UIEdgeInsets也是一个结构体类型,它包含top、left、bottom、right 4个成员变量,分别代表着该UIScrollView所需要显示内容在上、左、下、右的留白。例如,内容视图为灰色部分,它的大小为320×480,而ScrollView的大小只有320×460,由于内容视图超出了ScrollView可显示的大小,并且上方要留一部分空白显示其他控件,因此,需要滚动屏幕来查看内容,如图2-65所示。
图2-65 contentInset属性
3.contentOffset
该属性是一个CGPoint类型的值,CGPoint也是一个结构体类型,它包含x 、y两个成员变量,代表内容视图的坐标原点与该UIScrollView坐标原点的偏移量,如图2-66所示。
图2-66 contentOffset属性
4.delegate
该属性是一个id类型的值,它可以指定代理对象。在ScrollView中定义了一个UIScrollViewDelegate协议,该协议定义了许多可以监听UIScrollView滚动过程的方法,例如,要想监听ScrollView的缩放和拖曳,可以通过遵守UIScrollViewDelegate协议,指定ScrollView的代理对象来实现。UIScrollViewDelegate协议的定义方式如下所示。
@protocol UIScrollViewDelegate<NSObject>
@optional
// 滚动UIScrollView时就会调用该方法
- (void)scrollViewDidScroll:(UIScrollView *)scrollView;
// 缩放UIScrollView时就会调用该方法
- (void)scrollViewDidZoom:(UIScrollView *)scrollView;
// 即将拖曳UIScrollView时就会调用该方法
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView;
// 即将停止拖曳UIScrollView时就会调用该方法
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)
targetContentOffset;
// 停止拖曳UIScrollView时就会调用该方法
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
willDecelerate:(BOOL)decelerate;
// UIScrollView即将减速时就会调用该方法
- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView;
// UIScrollView减速完成时就会调用该方法
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView;
// 返回缩放的视图,这个视图必须是UIScrollView的子视图
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView;
// UIScrollView即将缩放时就会调用该方法
- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView
withView:(UIView *)view;
// UIScrollView完成缩放时就会调用该方法
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:
(UIView *)view atScale:(CGFloat)scale;
@end
从上述代码中可以看出,UIScrollViewDelegate协议中定义了许多供代理监听的方法,这些方法会在滚动视图的不同状态下被调用。例如,scrollViewDidScroll方法是改变滚动视图偏移量时调用的方法,该方法会在视图滚动后执行。
2.9.2 实战演练——喜马拉雅
UIScrollView在iOS开发中经常使用,它主要用于在有限的屏幕上展示更多的内容。为了大家更好地掌握UIScrollView的使用,接下来带领大家搭建一个喜马拉雅的应用界面,具体步骤如下。
1.界面设计
(1)新建一个Single View Application应用,名称为10_UIScrollView,然后在Main.storyboard界面中添加1个UIScrollView、2个UIView、1个UILabel和12个UIButton,其中UIButton只作为显示,不支持单击事件,UIScrollView用于滚动它内部包含的内容视图。
(2)将提前准备好的图片放到Images.xcassets文件中,并为UIButton设置普通和高亮状态下的背景图片,设计好的界面如图2-67所示。
在图2-67中,UIScrollView上添加了7个UIButton,它们分别设置了普通状态下的背景图片,由于屏幕尺寸的限制,无法展示UIScrollView最底部的一个UIButton。需要注意的是,只有添加到UIScrollView内部的控件才能实现滚动,如同绑定的关系。
图2-67 搭建好的喜马拉雅界面
2.创建控件对象的关联
单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,使用控件和代码关联的方式,为UIScrollView和其内部最底部的UIButton添加两个属性,分别命名为scrollView和lastView,添加完成后的界面如图2-68所示。
图2-68 创建视图对象的关联
从图2-68中可以看出,完成控件对象的关联后,成功添加了两个属性。
3. 通过代码实现滚动的功能
完成控件对象的关联后,就可以通过代码实现滚动的功能了。进入ViewController.m文件,在viewDidLoad方法中实现滚动的功能,代码如例2-10所示。
【例2-10】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()
3 // 定义了两个属性scrollView和lastView,分别表示滚动视图和其内部最底部的视图
4 @property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
5 @property (weak, nonatomic) IBOutlet UIButton *lastView;
6 @end
7 @implementation ViewController
8 - (void)viewDidLoad {
9 [super viewDidLoad];
10 // 1.获取lastView的最大Y值
11 CGFloat lastViewH = CGRectGetMaxY(self.lastView.frame) + 10;
12 // 2.设置scrollView的滚动范围
13 self.scrollView.contentSize = CGSizeMake(0, lastViewH);
14 // 3.设置scrollView的偏移量
15 self.scrollView.contentOffset = CGPointMake(0, -54);
16 // 4.设置scrollView的间距
17 self.scrollView.contentInset = UIEdgeInsetsMake(54, 0, 44, 0);
18 }
19 @end
在例2-10中,第8~18行代码是viewDidLoad方法,该方法中首先根据最底部视图的Y值确定scrollView的滚动范围,然后设置scrollView的偏移量和间距,协调滚动视图跟其他视图的位置,使界面更加美观。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,发现视图可以滚动了,并且最底部的视图也能显现出来,滚动界面的部分场景图片如图2-69所示。
图2-69 滚动界面
2.10 页控件(UIPageControl)
2.10.1 页控件概述
顾名思义,页控件是一个可以实现翻页效果的控件,它是一个比较简单的控件,由N个小圆点组成,每个小圆点代表一个页面,并且当前页面使用高亮的圆点显示。在iOS中,页控件使用UIPageControl类来表示,它直接继承于UIControl:UIView,是一个可以与用户交互的活动控件。接下来通过一张图片来展示UIPageControl的使用场景,如图2-70所示。
图2-70 UIPageControl的使用场景
图2-70所示了5个小圆点,并且第2个小圆点是高亮状态,说明页控件包含5个页面,并且当前页面是第2个页面。
页控件Page Control同样可以从对象库中找到。将Page Control控件从对象库中拖曳到Main.storyboard编辑界面中,在Xcode右侧查看UIPageControl的属性检测器面板,如图2-71所示。
图2-71 UIPageControl的属性检测器面板
图2-71显示的是UIPageControl所支持的一些属性,通过对这些属性的设置,可以使页控件发生相应的变化。
针对UIPageControl属性检查器面板设置的属性,UIPageControl类也定义了与之对应的属性,接下来通过一张表来列举UIPageControl的常见属性,如表2-16所示。
表2-16 UIPageControl的常见属性
属性声明 |
功能描述 |
---|---|
@property(nonatomic) NSInteger numberOfPages; |
设置总共有多少页 |
@property(nonatomic) NSInteger currentPage; |
设置当前是第几页 |
@property(nonatomic,retain) UIColor *pageIndicatorTintColor; |
设置页码指示器的颜色 |
@property(nonatomic,retain) UIColor *currentPageIndicatorTintColor; |
设置当前页码指示器的颜色 |
表2-16列举了UIPageControl所支持的一些属性,它们均可以设置页控件的外观。
2.10.2 实战演练——自动轮播器
实际项目中,经常会把UIScrollView和UIPageControl结合使用,接下来,带领大家使用这两个控件完成一个自动轮播器,具体步骤如下。
1.创建工程,设计界面
(1)新建一个Single View Application应用,名称为10_UIScrollView和UIPageControl,然后在Main.storyboard界面中添加一个UIScrollView、一个UIPageControl、一个UIView和一个UILabel,其中,UIScrollView用于显示轮播图片,UIPageControl用于显示页码。
(2)将提前准备好的图片放到Images.xcassets文件中,并为UIPageControl的圆点和高亮圆点分别设置白色和蓝色,设计好的界面如图2-72所示。
图2-72 搭建好的界面
2.创建控件对象的关联
(1)单击Main.storyboard左下角的图标,打开文档大纲区,选中UIScrollView右击,弹出一个黑框列表,从该列表delegate选项后的空圆圈拖线到文档大纲区中的控制器文件,设置UIScrollView的delegate为控制器,如图2-73所示。
图2-73 设置UIScrollView的delegate为控制器
(2)单击Xcode 6.1界面右上角的图标,进入控件与代码的关联界面,使用控件和代码关联的方式,为UIScrollView和UIPageControl添加两个属性,分别命名为scrollView和pageControl,用于表示轮播器和页码指示器,添加完成后的界面如图2-74所示。
图2-74 创建UIScrollView和UIPageControl控件对象的关联
3. 通过代码实现自动轮播的功能
完成控件对象的关联后,就可以通过代码加载图片,并实现自动轮播的功能了。进入ViewController.m文件,在viewDidLoad方法中完成相对的功能,代码如例2-11所示。
【例2-11】ViewController.m
1 #import "ViewController.h"
2 @interface ViewController ()<UIScrollViewDelegate>
3 // 定义两个属性scrollView和pageControl,分别表示轮播器和页码
4 @property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
5 @property (weak, nonatomic) IBOutlet UIPageControl *pageControl;
6 @property (nonatomic, strong) NSTimer *timer; // 定时器
7 @end
8 @implementation ViewController
9 - (void)viewDidLoad {
10 [super viewDidLoad];
11 // 1.图片的总数
12 int count = 6;
13 // 2.imageView的尺寸和Y值
14 CGFloat imageY = 0;
15 CGFloat imageW = self.scrollView.frame.size.width;
16 CGFloat imageH = self.scrollView.frame.size.height;
17 // 3.循环添加5张图片到scrollView上
18 for (int i = 0; i<count; i++) {
19 UIImageView *imageView = [[UIImageView alloc] init];
20 CGFloat imageX = i * imageW;
21 imageView.frame = CGRectMake(imageX, imageY, imageW, imageH);
22 // 拼接图片的名称
23 NSString *imageName = [NSString
24 stringWithFormat:@"img_0%d",i+1];
25 imageView.image = [UIImage imageNamed:imageName];
26 [self.scrollView addSubview:imageView];
27 }
28 // 4.设置scrollView的contentSize,让视图可以滚动
29 CGFloat contentW = count * imageW;
30 self.scrollView.contentSize = CGSizeMake(contentW, 0);
31 // 5.隐藏scrollView的水平滚动条
32 self.scrollView.showsHorizontalScrollIndicator = NO;
33 // 6.设置pageControl的总页数
34 self.pageControl.numberOfPages = count;
35 // 7.设置scrollView分页
36 self.scrollView.pagingEnabled = YES;
37 // 8.开启定时器
38 [self addTimer];
39 }
40 //添加定时器方法
41 - (void)addTimer
42 {
43 self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0f
44 target:self selector:@selector(nextImage) userInfo:nil repeats:YES];
45 [[NSRunLoop currentRunLoop] addTimer:self.timer
46 forMode:NSRunLoopCommonModes];
47 }
48 //移除定时器方法
49 - (void)removeTimer{
50 [self.timer invalidate];
51 self.timer = nil;
52 }
53 // 定时器调用的方法
54 - (void)nextImage
55 {
56 int count = 6;// 图片的总数
57 // 增加pageControl的页码
58 int page = 0;
59 if (self.pageControl.currentPage == count - 1) {
60 page = 0;
61 } else {
62 page = self.pageControl.currentPage + 1;
63 }
64 // 计算scrollView滚动的位置
65 CGFloat offsetX = page * self.scrollView.frame.size.width;
66 CGPoint offset = CGPointMake(offsetX, 0);
67 [self.scrollView setContentOffset:offset animated:YES];
68 }
69 #pragma mark - UIScrollViewDelegate方法
70 //当scrollView正在滚动就会调用该方法
71 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
72 {
73 // 根据scrollView的滚动位置决定pageControl显示第几页
74 CGFloat scrollW = scrollView.frame.size.width;
75 int page = (scrollView.contentOffset.x+scrollW*0.5)/scrollW;
76 self.pageControl.currentPage = page;
77 }
78 //开始拖曳的时候会调用该方法
79 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
80 {
81 // 停止定时器(一旦停止,就不能再使用)
82 [self removeTimer];
83 }
84 //停止拖曳的时候会调用该方法
85 - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView
86 willDecelerate:(BOOL)decelerate
87 {
88 [self addTimer]; // 开启定时器
89 }
90 @end
在例2-11中,第9~39行代码是viewDidLoad方法,用于设置滚动视图和页码内容;第41~52行代码为添加和移除定时器的方法;第54~68行代码为定时器每间隔2秒重复调用的方法,用于切换高亮圆点的位置;第71~89行代码均为UIScrollViewDelegate方法,分别用于设置滚动视图的分页、用户拖曳时停止定时器和停止拖曳时启动定时器。
4. 在模拟器上运行程序
单击Xcode工具的运行按钮,在模拟器上运行程序。程序运行成功后,每隔两秒钟自动切换下一张图片,一个自动播放图片的应用开发完成了,自动轮播器的部分场景如图2-75所示。
图2-75 自动轮播器的部分场景
2.11 本章小结
本章首先对UI开发的始祖UIView进行了详细讲解,然后介绍了iOS开发中常用的控件,包括标签控件、图片控件、按钮控件、文本控件、开关控件、滑块控件、分段控件、数据选择控件、屏幕滚动控件和页控件,在讲解这些控件时,采用的方式都是从生活引入开发的方式,让大家带着对每个控件的基本认识去学习,并带领大家使用不同的控件开发不同的案例。
通过本章的学习,希望大家可以熟练掌握这些控件的使用,能够独立完成相关案例的开发,从而加强本章知识的学习。
【思考题】
1 . 简述什么是懒加载。
2 . 简述UIView中frame和bounds属性的区别。
扫描右方二维码,查看思考题答案!