1、 UIPickerView简介
UIPickerView也是一个iOS开发中常用的控件,适用于让用户选择少量数据,比如设置出生日期、城市、国家等。UIPickerView在iOS6和iOS7后的风格有所改变,之前是拟物化,现在变为扁平化了。他的用法和我们之前学习的UITableView类似,都需要用到数据源和代理来显示数据和监听事件。
//1.UIPickerView的常见属性
// 数据源(用来告诉UIPickerView有多少列多少行)
@property(nonatomic,assign) id<UIPickerViewDataSource> dataSource;
// 代理(用来告诉UIPickerView每1列的每1行显示什么内容,监听UIPickerView的选择)
@property(nonatomic,assign) id<UIPickerViewDelegate> delegate;
// 是否要显示选中的指示器
@property(nonatomic) BOOL showsSelectionIndicator;
// 一共有多少列
@property(nonatomic,readonly) NSInteger numberOfComponents;
//2.UIPickerView的常见方法
// 重新刷新所有列
- (void)reloadAllComponents;
// 重新刷新第component列
- (void)reloadComponent:(NSInteger)component;
// 主动选中第component列的第row行
- (void)selectRow:(NSInteger)row inComponent:(NSInteger)component animated:(BOOL)animated;
// 获得第component列的当前选中的行号
- (NSInteger)selectedRowInComponent:(NSInteger)component;
//3.数据源方法(UIPickerViewDataSource)
// 一共有多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView;
// 第component列一共有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component;
//4.代理方法(UIPickerViewDelegate)
// 第component列的宽度是多少
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component;
// 第component列的行高是多少
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component;
// 第component列第row行显示什么文字
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component;
// 第component列第row行显示怎样的view(内容)
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view;
// 选中了pickerView的第component列第row行
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component;
UIPickerView的注意点
1.在ios8之前,UIPickerView的高度是(162)固定的,不能被修改。在 ios8后,它的高度就可以修改了。
2.下面这个方法的重用 view 在 ios6之后就一直为空。如果需要适配 ios6之前的系统,为了性能考虑,应该将这个 view 利用起来。
//重用View在iOS6之前才有效
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view;
pickerView的高度设置
如果没有主动设置高度,或者设置为0。则高度默认值216
如果设置的高度大于0且小于180,则高度默认值162
如果设置的高度大于等于180且小于216,怎么高度默认值为180
pickerView 的宽度设置
初始化时没有给定宽度,或者给定的宽度为0,则宽度默认等于屏幕的宽度。
给定了一个不为大于0的宽度,则宽度就等于给定的值。
2、 UIPickerView显示字符串
UIPickerView可以显示纯文字,也能显示自定义View。我们用一个简单的案例来演示UIPickerView的基本使用方法,需求是实现滚动选择数据,并将选中的数据赋值给指定的控件,并提供一个随机选择的方法。最终效果图如下所示:
首先我们直接在Main.storyboard中搭建界面,由于只是演示,我就只做了UIPickerView的适配。
我们将选择器中所要展示的数据存储在plist文件中了,并且没有用到字典,所以无需封装模型直接先懒加载数据存储到一个数组中即可。
ViewController.m
#import "ViewController.h"
@interface ViewController () <UIPickerViewDataSource,UIPickerViewDelegate>
//UIPickerView控件
@property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
//用于显示数据的Label
@property (weak, nonatomic) IBOutlet UILabel *categoriesLbl;
@property (weak, nonatomic) IBOutlet UILabel *timeLbl;
@property (weak, nonatomic) IBOutlet UILabel *constellationLbl;
//存储从plist加载进来的数据的数组
@property (strong, nonatomic) NSArray *filterArray;
//随机选择数据的方法
- (IBAction)randomSelect;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//设置UIPickerView的数据源、代理
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
//默认选中每列的第一行
for (NSInteger component = 0; component < self.filterArray.count; component++) {
[self pickerView:nil didSelectRow:0 inComponent:component];
}
}
//懒加载
-(NSArray *)filterArray {
if (!_filterArray) {
//从plist文件中加载数据
_filterArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"filter.plist" ofType:nil]];
}
return _filterArray;
}
//随机选择
- (IBAction)randomSelect {
for (NSInteger component = 0; component < self.filterArray.count; component++) {
//取出第component列的子数组的count
NSInteger count = [self.filterArray[component] count];
//返回第component列中选中的行
NSInteger currentSelectedRow = [self.pickerView selectedRowInComponent:component];
//假设随机生成的行和当前行相等
NSInteger randomRow = currentSelectedRow;
//随机生成一个和当前选中行不同的行号
if (randomRow == currentSelectedRow) {
randomRow = arc4random_uniform((u_int32_t)count);
}
//主动选中数据
[self.pickerView selectRow:randomRow inComponent:component animated:YES];
//手动调用这个方法,将选中的数据赋值给Label
[self pickerView:self.pickerView didSelectRow:randomRow inComponent:component];
}
}
//返回一共多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return self.filterArray.count;
}
//返回component列的行数
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return [self.filterArray[component] count];
}
//返回component列的row行的title
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
return self.filterArray[component][row];
}
//当选中component列的row行时调用
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
NSArray *date = self.filterArray[component];
NSString *title = date[row];
//将选中的数据赋值给对应的Label
if (component == 0) {
self.categoriesLbl.text = title;
} else if (component == 1) {
self.timeLbl.text = title;
} else if (component == 2) {
self.constellationLbl.text = title;
}
}
@end
3、 UIPickerView显示自定义View
用一个国旗选择的案例来演示如何让UIPickerView显示的数据是自定义View而不是纯字符串,其实和上面的案例区别不大,只是换了一个返回每列、每行数据如何显示的代理方法。上面案例是直接返回字符串,这里我们需要返回一个自定义View而已。下图是我们的需求:
首先创建项目导入plist文件,数据是以字典形式存储的,所以我们需要封装模型。
JFFlag.h
#import <Foundation/Foundation.h>
@interface JFFlag : NSObject
//国旗名称
@property (copy, nonatomic) NSString *name;
//国旗图片
@property (copy, nonatomic) NSString *icon;
//快速创建模型
- (instancetype)initWithDictionary:(NSDictionary *)dict;
+ (instancetype)flagWithDictyinary:(NSDictionary *)dict;
//返回模型数组
+ (NSArray *)flags;
@end
JFFlag.m
#import "JFFlag.h"
@implementation JFFlag
//快速创建模型
- (instancetype)initWithDictionary:(NSDictionary *)dict {
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
+ (instancetype)flagWithDictyinary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
//返回模型数组
+ (NSArray *)flags {
NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil]];
NSMutableArray *arrayM = [NSMutableArray array];
for (NSDictionary *dict in array) {
JFFlag *flag = [JFFlag flagWithDictyinary:dict];
[arrayM addObject:flag];
}
return arrayM;
}
@end
在Main.storyboard中搭建界面,这里我们只需要拖拽一个UIPickerView控件即可。
UIPickerView显示的自定义View,也就是类似于我们之前学过的UITableViewCell。这里我们也单独封装自定义View,并用xib来描述这个View,并进行属性连线。
然后实现封装类中的方法,提供一个快速创建自定义View的类方法和为自定义View的子控件赋值的方法。
JFFlagView.h
#import <UIKit/UIKit.h>
#import "JFFlag.h"
@interface JFFlagView : UIView
@property (strong, nonatomic) JFFlag *flag;
//快速创建自定义View
+ (instancetype)flagView;
@end
JFFlagView.m
#import "JFFlagView.h"
#import "JFFlag.h"
@interface JFFlagView ()
@property (weak, nonatomic) IBOutlet UIImageView *iconView;
@property (weak, nonatomic) IBOutlet UILabel *nameView;
@end
@implementation JFFlagView
//快速创建自定义View
+ (instancetype)flagView {
//从xib重创建View
return [[[NSBundle mainBundle] loadNibNamed:@"JFFlagView" owner:nil options:nil] lastObject];
}
//重写set方法为自定义View中的子控件赋值
- (void)setFlag:(JFFlag *)flag {
_flag = flag;
self.iconView.image = [UIImage imageNamed:flag.icon];
self.nameView.text = flag.name;
}
@end
最后我们在控制器中懒加载数据,并实现UIPickerView的数据源和代理方法,进行数据的展示。
ViewController.m
#import "ViewController.h"
#import "JFFlag.h"
#import "JFFlagView.h"
@interface ViewController () <UIPickerViewDataSource,UIPickerViewDelegate>
@property (strong, nonatomic) NSArray *flags;
@property (weak, nonatomic) IBOutlet UIPickerView *pickerView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//指定数据源和代理对象
self.pickerView.dataSource = self;
self.pickerView.delegate = self;
}
//懒加载
- (NSArray *)flags {
if (_flags == nil) {
_flags = [JFFlag flags];
}
return _flags;
}
//一共有多少列数据,注意和UITableView不同的是,就算是只有一列,这个方法也不能省略
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 1;
}
//每组有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.flags.count;
}
//返回每行的View
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
JFFlag *flag = self.flags[row];
//创建自定义View并赋值数据
JFFlagView *flagView = [JFFlagView flagView];
flagView.flag = flag;
return flagView;
}
//返回第componet列的行高
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
return 50;
}
@end
注意
:在iOS6之后,返回的View就不能重用了,因为苹果觉得UIPickerView展示的数据量都非常小,没有必须重用。如果是在iOS6之前,也可以重用View。
4、 UIDatePicker简介
UIDatePicker是一个日期选择器控件,他的用法和UIPickerView类似。UIDatePicker继承自UIControl,所以可以添加监听事件。但是有些童鞋认为UIDatePicker是UIPickerView的子类,事实不是这样的,只是在UIDatePicker内部封装了一个UIPickerView,所以不要弄错了啊。并且UIDatePicker控件经常和UIToolBar一起使用,也就是给日期选择器添加一个工具条。
//1.常见属性
// datePicker的显示模式
@property (nonatomic) UIDatePickerMode datePickerMode;
// 显示的区域语言
@property (nonatomic, retain) NSLocale *locale;
//2.监听UIDatePicker的选择
* 因为UIDatePicker继承自UIControl,所以通过addTarget:...监听
//3.代码创建UIDatePicker
// 1.创建日期选择器
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
// 2.设置日期显示区域
datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
// 3.设置日期模式
datePicker.datePickerMode = UIDatePickerModeTime;
// 4.0 设置最小时间(从当前时间起的前20年)
datePicker.minimumDate = [NSDate dateWithTimeIntervalSinceNow:-(365 * 24 * 3600 * 20)];
// 4.1 设置最大时间(从当前时间起的后20年)
datePicker.maximumDate = [NSDate dateWithTimeIntervalSinceNow:365 * 24 * 3600 * 20];
// 5.设置时间间隔。(该间隔要能够让60整除)
datePicker.minuteInterval = 6;
5、 UIPickerView基本使用方法
使用UIDatePicker和UIPickerView完成选择生日和省份的功能,自定义键盘其实就是为UITextField的inputView赋值一个自定义View,工具条则是为UITextField的inputAccessoryView赋值一个自定义的View。
自定义UITextField弹出的键盘:
这里第一个文本框的inputView赋值一个UIDatePicker,inputAccessoryView则是一个UIToolBar。
第二个文本框的inputView赋值一个UIPickerView,inputAccessoryView也是一个UIToolBar。
创建项目,在storyboard中搭建界面,这里只需要拖拽两个UILabel和UITextField即可,其他控件需要代码创建 。
搭建好界面先来创建模型,导入plist文件观察可轻易发现数据的结构。每一个字典中有两个键值对,一个是城市数组,另一个是省份字符串。由此可以确定,UIPickerView的第一列显示的数据就是所有省份,第二列显示的数据则是每个省份对应的城市,而不是固定的数据。
字典转模型,返回模型数组。已经写了无数遍了,就不再解释了。提供一个返回模型数组的类方法,方便懒加载时直接调用这个类方法返回模型数组。
Province.h
#import <Foundation/Foundation.h>
@interface Province : NSObject
@property(nonatomic,strong) NSArray *cities;
@property(nonatomic,copy) NSString *name;
//快速创建模型对象
+ (instancetype)provinceWithDict:(NSDictionary *)dict;
- (instancetype)initWithDict:(NSDictionary *)dict;
//返回模型数组
+ (NSArray *)provinces;
@end
Province.m
#import "Province.h"
@implementation Province
//快速创建模型对象
+ (instancetype)provinceWithDict:(NSDictionary *)dict{
return [[self alloc] initWithDict:dict];
}
- (instancetype)initWithDict:(NSDictionary *)dict{
if (self = [super init]) {
[self setValuesForKeysWithDictionary:dict];
}
return self;
}
//返回模型数组
+ (NSArray *)provinces{
NSArray *provinceArray = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"cities.plist" ofType:nil]];
NSMutableArray *provinces = [NSMutableArray array];
for (NSDictionary *dict in provinceArray) {
Province *province = [self provinceWithDict:dict];
[provinces addObject:province];
}
return provinces;
}
@end
封装好模型后,在控制器中懒加载模型数组。并完成其他一系列调用,具体代码如下:
ViewController.m
#import "ViewController.h"
#import "Province.h"
@interface ViewController ()<UIPickerViewDataSource,UIPickerViewDelegate,UITextFieldDelegate>
//文本框控件
@property (weak, nonatomic) IBOutlet UITextField *inputField;
@property (weak, nonatomic) IBOutlet UITextField *provinceField;
//日期选择器,注意懒加载的控件需要用strong而不是weak
@property(nonatomic,strong) UIDatePicker *datePicker;
//工具导航控件,注意懒加载的控件需要用strong而不是weak
@property(nonatomic,strong) UIToolbar *toolBar;
//模型数组
@property(nonatomic,strong) NSArray *provinces;
//当前选中的省份
@property(nonatomic,strong) Province *selectedProvince;
@end
@implementation ViewController
//懒加载模型数组
- (NSArray *)provinces {
if (_provinces == nil) {
_provinces = [Province provinces];
}
return _provinces;
}
//懒加载工具条控件
- (UIToolbar *)toolBar {
if (_toolBar == nil) {
//创建键盘工具条
UIToolbar *toolbar = [[UIToolbar alloc] init];
toolbar.frame = CGRectMake(0, 0, 320, 44);
toolbar.barTintColor = [UIColor orangeColor];
toolbar.tintColor = [UIColor whiteColor];
//上一个
UIBarButtonItem *lastItem = [[UIBarButtonItem alloc] initWithTitle:@"上一个" style: UIBarButtonItemStyleDone target:self action:@selector(lastClick)];
//下一个
UIBarButtonItem *nextItem = [[UIBarButtonItem alloc] initWithTitle:@"下一个" style: UIBarButtonItemStyleDone target:self action:@selector(nextClick)];
UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
//完成
UIBarButtonItem *complete = [[UIBarButtonItem alloc] initWithTitle:@"完成" style: UIBarButtonItemStyleDone target:self action:@selector( complete)];
toolbar.items = @[lastItem,nextItem,flexibleSpace,complete];
_toolBar = toolbar;
}
return _toolBar;
}
//懒加载时间选择器控件
- (UIDatePicker *)datePicker {
if (_datePicker == nil) {
//创建日期选择器
UIDatePicker *datePicker = [[UIDatePicker alloc] init];
//设置日期显示区域
datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
//设置日期模式
datePicker.datePickerMode = UIDatePickerModeDate;
//设置最小时间
datePicker.minimumDate = [NSDate dateWithTimeIntervalSinceNow:-(365 * 24 * 3600 * 20)];
//设置最大时间
datePicker.maximumDate = [NSDate dateWithTimeIntervalSinceNow:365 * 24 * 3600 * 20];
//设置时间间隔,默认为1,设置的值必须能被60整除
datePicker.minuteInterval = 6;
//监听工具条的点击
[datePicker addTarget:self action:@selector(datePickerClick:) forControlEvents:UIControlEventValueChanged];
_datePicker = datePicker;
}
return _datePicker;
}
- (void)viewDidLoad {
[super viewDidLoad];
//设置文本输入框的代理
self.inputField.delegate = self;
self.provinceField.delegate = self;
//默认显示当天日期
[self datePickerClick:nil];
}
#pragma mark - 数据源方法
//城市选择一共有多少列
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
return 2;
}
//城市选择第component列有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
if (component == 0) {
//第一列,显示所有省份,所以就是模型数组的长度
return self.provinces.count;
} else {
//获取当前第一列中选中的数据的索引
NSInteger provinceIndex = [pickerView selectedRowInComponent:0];
//根据这个索引获取当前选中的数据的模型对象
Province *province = self.provinces[provinceIndex];
//返回这个模型对象中cities属性的长度,也就是对应城市个数
return province.cities.count;
}
}
#pragma mark - 代理方法
//第component列的第row行显示什么数据
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component{
if (component == 0) {
//显示哪个省
Province *province = self.provinces[row];
return province.name;
} else {
//显示哪个城市
//获取当前第一列中选中的数据的索引
NSInteger provinceIndex = [pickerView selectedRowInComponent:0];
//根据这个索引获取当前选中的数据的模型对象
Province *province = self.provinces[provinceIndex];
if (row >= province.cities.count) return nil;
return province.cities[row];
}
}
//选中第 component 列的第row 行
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
if (component == 0) {
//改变了省份
self.selectedProvince = self.provinces[row];
//刷新第1列的数据(重新刷新数据,重新调用数据源和代理的相应方法获得数据)
[pickerView reloadComponent:1];
//选中第1列的第0行
[pickerView selectRow:0 inComponent:1 animated:YES];
}
NSString *cityName = self.selectedProvince.cities[component == 0 ? 0:row];
self.provinceField.text = [NSString stringWithFormat:@"%@ %@",self.selectedProvince.name,cityName];
}
#pragma mark - 监听按钮的点击事件
//完成按钮
- (void)complete{
//确保用户是选中了省份的
if ([self.provinceField isFirstResponder] && self.selectedProvince && self.provinceField.text.length == 0) {
self.provinceField.text = [NSString stringWithFormat:@"%@ %@",self.selectedProvince.name,self.selectedProvince.cities[0]];
}
[self.view endEditing:YES];
}
//上一个按钮
- (void)lastClick{
if (self.provinceField.isFirstResponder) {
[self.inputField becomeFirstResponder];
}
}
//下一个按钮
- (void)nextClick{
if (self.inputField.isFirstResponder) {
[self.provinceField becomeFirstResponder];
}
}
#pragma mark - 监听日期选择器时间的改变
- (void)datePickerClick:(UIDatePicker *)datePicker{
//获取NSDate对象
NSDate *date = datePicker == nil ? [NSDate date]:[datePicker date];
//创建时间格式化对象
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
//指定时间格式
formatter.dateFormat = @"yyyy-MM-dd";
//将时间赋值给文本框
self.inputField.text = [formatter stringFromDate:date];
}
#pragma mark - 文本输入框代理
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
if(textField.inputView) return YES;
if (textField == self.inputField) {
//自定义生日文本输入框的键盘
self.inputField.inputView = self.datePicker;
//给键盘添加配置工具条
self.inputField.inputAccessoryView = self.toolBar;
} else if(textField == self.provinceField) {
//默认被选中的省份为数组中的一个省份
self.selectedProvince = self.provinces[0];
//自定义省份文本输入框的键盘
UIPickerView *pickerView = [[UIPickerView alloc] init];
//指定数据源、代理对象
pickerView.dataSource = self;
pickerView.delegate = self;
//自定义省份输入框的键盘
self.provinceField.inputView = pickerView;
//给键盘添加配置工具条
self.provinceField.inputAccessoryView = self.toolBar;
}
return YES;
}
@end
6、 项目中的常见文件
Info.plist是整个项目的重要配置文件不能删除。
Localization native development region :本地化相关。
Bundle display name :程序安装后显示在iphone/ipad上的名字。
Icon file :程序的图标,Xcode5以前创建的项目有,一般用Icon.png,Xcode5以后创建的不在plist设置,在Images.xcassets设置。
Bundle version :程序版本号,AppStore每更新版本,版本要增加,内部项目管理的版本号,不对外。
Bundle versions string, short :用于itunes上显示的版本号,即对外的版本。一般3个数组成。
Bundle identifier :应用的惟一标识,发布到AppStore去。
pch文件干什么用?
pch文件里的内容被项目中的其它所有资源共享访问,定义宏 身高、电话和其它文件共享使用、自定义日志输出。我们在开发阶段,在pch中使用DEBUG模式自定义日志输出宏,当app发布后这些宏就失效。
#ifdef DEBUG //开发阶段
#define Log(...) NSLog(__VA_ARGS__)
#else //发布阶段
#define Log(...)
#endif
因为pch中的内容会呗项目中其它所有资源共享访问,所以有可能我们项目中不只是有oc文件,还会有其它语言文件。所以我们在定义被共享的内容时,应该加判断
#ifdef __OBJC__
//这里的内容是只有.m、.mm文件中才能访问
#endif
注意:一般公用的资源写在#ifdef __OBJC__里面。
7、 App启动原理
UIApplication
什么是UIApplication?
1.UIApplication是整个应用程序的象征,就像中国的象征是五星红旗。
2.每一个应用都有自己的UIApplication,而且是单例,通过[UIApplication sharedApplication]获取。单例对象也就是程序运行到结束,只能有一个对象。如果我们使用[[UIApplication alloc] init]创建对象,程序不被允许并且会直接报错,因为application只有能一个对象。
3.ios程序启动后创建的第一人对象就是UIApplication对象。
UIApplication用来用来设置全局性的东西
设置网络请求状态/取消网络请求状态
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
设置应用图标数字/清除图标数据
[UIApplication sharedApplication].applicationIconBadgeNumber = 2;
不过在iOS8更新后不能直接这样设置应用图标的数字了,需要先获得用户的授权,才能显示。完成代码如下:
//创建单例对象
UIApplication *app = [UIApplication sharedApplication];
//判断系统版本是否超过8.0
if ([[[UIDevice currentDevice] systemVersion] doubleValue] >= 8.0) {
//如果超过就要获取用户的授权
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeBadge categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
//设置数字
app.applicationIconBadgeNumber = 10;
设置状态栏样式
方式1.由控制器的一个方法决定
- (UIStatusBarStyle)preferredStatusBarStyle;
方式2.使用application设置
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
但是这样设置默认是不起作用的,因为默认状态栏样由控制器来管理,如果想用application设置状态栏有效,得在Info.plist的设置View controller-based status bar appearance = NO。
打电话、发短信、发邮件、打开网站
调用application的openURL方法即可,这里就只演示打开网站,其他同理。
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"mailto://1012@qq.com"]];
UIWindow
什么是UIWindow?
1.UIWindow是用来显示控制器的View的。
2.每一个应用程序都有一个窗口。
演示UIWindow,在没有设置主要storyboard的情况下
首先在info.plist文件中清空Main storyboard file base name的value。
在AppDelegate.m文件中的didFinishLaunchingWithOptions方法中创建窗口,设置为主窗口并可见。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//创建窗口
UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//设置颜色
window.backgroundColor = [UIColor purpleColor];
//窗口为什么要有根控制器,是因为窗口上要显示视图
UIViewController *vc = [[UIViewController alloc] init];
vc.view.backgroundColor = [UIColor grayColor];
window.rootViewController = vc;
//这一步内部其实是添加控制器的view到窗口上
//[window addSubview:vc.view]
//窗口显示的时候,一定要设置为主窗口并可见
[window makeKeyAndVisible];
self.window = window;
return YES;
}
效果如下:
窗口是一个特殊的UIView对象,可以往window添加子控件,如label、switch等等。但是一般不会在窗口添加子控件,会设置窗口的rootViewController属性,将控制器的view添加到窗口上。
注意:如果直接把控制器的view添加到窗口是不能让控制的view进行旋转的,但设置窗口的根控制器,控制器的view可以旋转。因为旋转事件传递是由UIApplication -> UIWindow -> 根控制器,窗口不会做旋转处理,只有控制器才会。所以别直接将UIView添加到UIWindow上面。
获取主窗口方式
一个窗口当前能接受键盘和非触摸事件时,便被认为是主窗口。还有下面三种方式都能获取主窗口:
[UIApplication sharedApplication].delegate.window
[UIApplication sharedApplication].keyWindow
self.view.window
上面的情况是在info.plist中没有指定主要的storyboard,才需要手动创建UIWindow,并创建控制器赋值给UIWindows的rootViewController属性,再设置UIWindow为主窗口并显示。
还有一种情况就是在已经在info.plist中指定了主要的storyboard,这样程序会自动创建窗口,设置窗口的根控制器为storyboard的控制器,让窗口成为主窗口并显示。
App启动原理
我们从第一天开始学习C语言就知道,程序是入口是main函数,OC也不例外!所以程序运行首先执行main函数,main函数就一行代码,不过这一行代码可做了不少事情,main函数如下:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
1.程序运行后根据main函数中第三个参数创建UIApplication对象,UIApplication是程序中创建的第一个对象。这个参数如果为nil默认就是UIApplication,如果换成我们自己创建的类,就必须继承自UIApplication。
2.根据main函数中第四个参数创建UIApplication的代理对象,并赋值给UIApplication对象的delegate属性,并开启Main Runloop(事件循环)。
3.进行事件的处理,首先会在程序启动完毕后调用delegate对象的application:didFinishLaunchingWithOptions:方法。此时要分两种情况进行处理了,首先会去info.plist文件中查找是否设置了主要的storyboard文件名。
如果没有设置storyboard:我们需要手动在AppDelegate.m中的application:didFinishLaunchingWithOptions:中创建UIWindow,然后创建和设置UIWindow的rootViewController。最后设置UIWindow为主窗口并可见。
已经设置storyboard:根据Info.plist获得最主要storyboard的文件名,加载最主要的storyboard。自动创建UIWindow,自动创建和设置UIWindow的rootViewController为当前storyboard的控制器(再次提醒:这一步其实是将rootViewController中控制器的view添加到窗口上显示)。最后设置UIWindow为主窗口并可见。