您的位置:首页 > 移动开发 > IOS开发

IOS开发笔记35-多控制器管理(1)

2015-10-31 14:23 344 查看

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为主窗口并可见。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: