基本搭建
- 1.必学三个东西
1.主流的框架
2.引导页
3.设置界面 - 2.两种开发方式:
1.Storyboard(界面太多,不好找)
2.纯代码(界面比较多的时候用纯代码,利于后期扩展) - 3.回顾一下程序启动的思路 首先程序的入口是
- 1.main函数-> UIApplicationMain ->
- 2.创建UIApplication对象
- 3.创建UIApplication对象代理
- 4.开启主运行循环,保持程序一直运行
- 5.加载info.plist文件,判断下是否有main
- 4.创建一个窗口
- 1.创建窗口
- 2.设置窗口的跟控制器(UITabBarVc)
- 3.让主窗口显示
- 5.简化一下我们项目的内存
- 干掉没有用的类
- 6.工程配置
- 1.Identity
- 1.工程的前缀
- 2.Bundle identiffier作用
- a.程序上传AppStore
- b.push
- c.应用更新的时候
- 3.Vresion项目迭代开发:基于之前的版本开发 例如1.0 1.1
- 4.Build版本号 和Version共同使用,一般在开发中设置为自增id 具体看
- 2.Deployment info(部署信息)
- 1.Target(部署目标) 支持最低的iOS系统的版本号
- 2.Devices 标识这个项目将来是部署在iPhone上,还是iPad上,还是两者都有
- 7.LaunchScreen > LaunchImages
- 8.做项目的步骤
- 1.主框架
- 2.开发方式
- 3.环境的部署
基本架构
1.自定义:
- 1.自定义类 在开发中控制器基本都需要自定义(用于处理复杂的业务逻辑),方便调试bug
- 2.自定义控件
当系统的控件不满足需求的时候自定义系统控件,但是要还原系统的方法,
例如: 自定义模型,系统没有给出满足我们需求的类
自定义button要求:按钮的图片在右边,文字在左边
2.划分文件夹的架构(MVC)
- 1.一定要记得谁得事情谁管理(自己的事情自己做)
- 2.项目的文件应该交给文件夹管理(MVC)
3.架构(MVC)好处
- 1.让更多功能复用
- 2.方便多人开发,项目结构更加清晰
- 3 方便以后统一管理,代码出错(改需求)快速定位到指定的模块
自定义tabBar1
1.自定义TabBar
2.创建5个控制器
3.给TabBar添加子控制器
+ 1.tabBar上的内容有对应的子控制器决的tabBarItem决定
2.tabBarItem 是模型
自定义tabBar2
- 1.因为UI妹纸给的图片超出了系统支持的最大尺寸具体看官方文档: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/IconMatrix.html
- 2.解决办法
- 1.和PM(产品经理)协商,公司有负责人和负责人协商改需求
- 2.协商不通过, 自己苦逼的想办法(一般项目需求定下来,非特殊情况,不会轻易改动)
- 3.系统的tabBar的缺陷,需要自定义
- 1.移除整个tabBar简单粗暴(暂用第一种方法)
- 1.移除系统的tabBar,添加自己的tabBar
- 1.1 TabBar用UIView代替(因为系统的TabBar不好用)
- 1.2 子控键用UIButton代替
- 1.3 切换子控制器 selectedIndex
- 1.移除系统的tabBar,添加自己的tabBar
- 2.移除系统tabBar子控件
- 1.移除整个tabBar简单粗暴(暂用第一种方法)
自定义tabBar3
- 1.添加几个按钮应当有外界决定,tabBar按钮的个数应该有tabBarController 的子控制器的个数决定
- 2.初始化的时候创建(这种方法不采用)
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
// 如果在这里写死,外面改了里面还的改
for (int i = 0; i < 5; i++) {
// 创建button
}
}
return self;
}
3.给TabBar增加一个count属性(这种方法不采用)
- (void)setCount:(NSInteger)count{
_count = count;
// 这种方法虽然能让外界传递参数,但是button的内容没有
for (int i = 0; i < count; i++) {
// 创建button
}
}
4.给TabBar增加一个模型数组(采用这种方法)这种方法即告诉TabBar有几个子控件,还有子控件的内容是什么,
- (void)setItems:(NSArray *)items{
_items = items;
for (UITabBarItem *item in items) {
UIButton *button = [[TabBarButton alloc] init];
[self addSubview:button];
[button setBackgroundImage:item.image forState:UIControlStateNormal];
[button setBackgroundImage:item.selectedImage forState:UIControlStateSelected];
[button addTarget:self action:@selector(tabbarOnClick:) forControlEvents:UIControlEventTouchDown];
}
}
1.传一个数组,数组中存放的是Items模型
2.设置图片一定要setBackgroundImage 设置背景图片,这样才能占满整个按钮
3.点击事件用UIControlEventTouchDown(按下就选中)
4.UIButton 默认有高亮状态,禁止高亮装态
1.自定义按钮
2.重写按钮的setHighlighted方法
// 禁止高亮状态
- (void)setHighlighted:(BOOL)highlighted{};
5.选中button的三部曲
1.取消上一次的选中
2.设置当前按钮选中
3.记录当前选中的按钮
6.在layoutSubviews里面调整按钮的frame
- (void)layoutSubviews{
[super layoutSubviews];
CGFloat buttonX = 0;
CGFloat buttonY = 0;
CGFloat buttonW = self.frame.size.width / self.items.count;
CGFloat buttonH = self.frame.size.height;
int i = 0;
for (UIButton *button in self.subviews) {
buttonX = buttonW * i;
// 传递索引
button.tag = i;
button.frame = CGRectMake(buttonX, buttonY, buttonW, buttonH);
if (i == 0){
button.selected = YES;
// 记录这次选中的按钮
self.seleButton = button;
}
i++;
}
}
切换控制器
- 1.切换控制器
1.调用self.selectedIndex,方法切换子控制器
2.用代理通知控制器我已经点击了,你要做什么那是你的事情(切换控制器)
3.用button的tag传递索引
设置导航条内容
- 1.包装成导航控制器 让普通的控制器成为导航控制器的根控制器
- 2.通过[navigationBar setBackgroundImage:] 设置背景图片,
- 3.UIBarMetrics模式
- 1.UIBarMetricsDefault,导航控制器控制器的子控制器的view的大小 = 屏幕的高度 - 64 如果要想设置导航条的背景图片,只能用这种模式
- 2.UIBarMetricsCompact,导航控制器的子控制器的view的大小 = 屏幕的高度,这种模式下如果设置了导航条的背景图片,没有效果
- 4.设置导航条的颜色标题
- 1.TextAttributes 复文本
- 2.通过[navigationBar setTitleTextAttributes:] 设置文字属性
NSForegroundColorAttributeName 设置字体颜色
NSFontAttributeName 设置字体大小
自定义导航控制器
- 1.如果在viewDidLoad 里面调用会调用5次(因为创建了5个导航控制器),如果想调用一次就在initialize里面设置
- 2.initialize方法
当前类或者他的子类第一次使用的时候调用
作用:初始化一个类 - 3.load方法
当程序一启动的时候就会调用,当前类只会调用一次
作用: 把类加载进内存,放在代码区 - 4.一般不在load方法里面写东西,因为load方法是在程序一启动的时候调用的,这个时候自动释放池还没有创建,所以创建出来的对象需要手动释放
- 5.appearance 导航条的标志
// 导航条标志
UINavigationBar *bar = [UINavigationBar appearance];
- 1.appearance是一个协议,只要遵守这个协议的类,都具备这个功能
- 2.UIButton UILabel 也有这个功能
- 3.如果用appearance就会导致重大bug,整个工程的导航条都一样
- 4.我们只要管好自己的事情就可以了 谁用我的导航控制器,我就改下面的类的导航条
- 5.采取这种方法
// appearanceWhenContainedIn获取当前类的标志
UINavigationBar *bar = [UINavigationBar appearanceWhenContainedIn:self, nil];
// iOS9以后新出的方法
// Classes 要获取哪些类的属性
UINavigationBar *bar = [UINavigationBar appearanceWhenContainedInInstancesOfClasses:@[self]];
图片渲染
// 不要将图片渲染
UIImage *image = [UIImage imageNamed:@"CS50_activity_image"];
image = [image imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
自定义蒙版
1.蒙版一般添加在keyWidown上面
2.pop菜单加载keyWidown上面,如果添加到蒙版上面,会随着父控件的透明而透明
3.解决不透明的方法,不要修改alpha,修改背景颜色就可以了
cover.backgroundColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:0.7f];
自定义pop菜单
- 1.popMenu是静态的,搞一个xib描述他
// 加载xib
PopMenuView *popMenu = [[NSBundle mainBundle] loadNibNamed:@"PopMenuView" owner:nil options:nil][0];
- 2.popMenu动画,1.移动自己,2.缩小自己
1.子控件不会随着父控件的缩小而缩小,
2.利用CALayer的一个属性,超出父控件剪切
// 1.先自己缩小在坐上角
self.center = CGPointMake(44,44);
// 这两种方法是一样的 clipsToBounds的底层实现也是 layer.masksToBounds
self.layer.masksToBounds = YES;
self.clipsToBounds = YES;
3利用transform动画
// 2.设置一个最小值,就会实现动画
self.transform = CGAffineTransformMakeScale(0.01, 0.01);
- 3.由谁添加,由谁移除
1.菜单是有控制器添加的,所以应该有控制器移除,用代理通知外界,已经点击了关闭按钮
2.给别人提供了一个显示的方法,也应该提供一个隐藏的方法,显示到那个位置,隐藏到那个位置应该有外界决定
block简单讲解
1.block 可以预先封装一份代码,在需要的时候调用
2.block定义的两种方法
// 1.第一种方法 这里的block是变量名
void(^block)() =^(){
// 要保存的代码块
};
// 2.起别名的方式 这里的MyBlock是类型名,这个可以跨方法执行
typedef void(^MyBlock)();
@property (nonatomic, strong) MyBlock block;
2.inline 创建block的快捷键
3.block当做参数来传递(最常用)
抽取UIView分类
1.在分类的.h文件中可以用 @property (nonatomic, assign) CGFloat x; 这样他只会生成get,set方法,并不会生成下划线的成员属性, get,set方法需要我们在.m文件中实现
-
2.pch
- 1.在Xcode6以后默认是不加载pch的,我们要告诉他,提前编译的时候加载pch 在build setting里面 搜索prefix
* 2.在工程中搜前缀(prefix)
* 3.pch的路径是工程路径下面的相对路径
- 3.在pch里面判断是否是 OBJC,如果是OBJC再倒入头文件
我的彩票
主要是拉伸图片
1.创建xib描述我的彩票
-
2.xib的名字和控制器的名字一样
- 1.设置file Owner 文件拥有者是MyLotteryViewController控制器
-
2.设置xib的View
3.x ,y 从那里拉伸, width,height拉伸多少,0代表拉伸1像素
4.sizeToFit(自适应:根据文字,图片,自动计算尺寸), 其他控件也有这个,例如UILabel
竞技场
loadView和segment的使用
-1.添加背景图片
- 2.在lodView里面自定义控制器的view
// 自定义控制器的view
- (void)loadView{
// 这里面一定不要调用self.view 如果调用会造成死循环
// self.view 底层会判断_view有没有创建,如果没有创建会掉loadView方法,这样会造成死循环
// UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:self.view.bounds];
UIImageView *bgImageView = [[UIImageView alloc] initWithFrame:[UIScreen mainScreen].bounds];
bgImageView.image = [UIImage imageNamed:@"NLArenaBackground"];
// 一定要设置 UIImageView 能与用户交互
bgImageView.userInteractionEnabled = YES;
self.view = bgImageView;
}
2.自定义导航控制器(竞技场是不同的风格)
3.设置titleView
添加segMentController
1.设置背景图片 setBackgroundImage
2.设置文字颜色 setTitleTextAttributes
3.设置前景色 tintColor
4.默认选中第0个;
发现界面搭建
- 1.创建stroyboard
加载stroyboard
//1.加载storyboard,(注意:这里仅仅是加载名称为DidscoverTableViewController的storyboard,并不会创建storyboard中的控制器和控件)
UIStoryboard *stroyboard = [UIStoryboard storyboardWithName:@"DidscoverTableViewController" bundle:nil];
//2.这个方法代表着加载storyboard中箭头指向的控制器(初始控制器)
DidscoverTableViewController *discoverVc = [stroyboard instantiateInitialViewController];
2.cell动画(从右到左cell 进入的动画效果)
在 willDisplayCell cell将要现实的时候这个方法里面做
cell.transform = CGAffineTransformMakeTranslation(self.view.width, 0);
// 还原
[UIView animateWithDuration:0.5f animations:^{
cell.transform = CGAffineTransformIdentity;
}];
自定义标题按钮
- 1.图片在右文字在左,需要自定义按钮
第一种方法
// 调整控件的 label 的位置
- (CGRect)titleRectForContentRect:(CGRect)contentRect
// 调整子控件的imageView 的位置
- (CGRect)imageRectForContentRect:(CGRect)contentRect
第二种方法在layoutSubviews里面调整子控件的位置
因为这个方法会调用多次所以加个判断
if (self.titleLabel.x > self.imageView.x) {
// 调整 titleLabel的位置
self.titleLabel.x = self.imageView.x;
// 调整imageView 的位置
// imageView 的x值 = label 的 最大x值
self.imageView.x = CGRectGetMaxX(self.titleLabel.frame);
}
- 2.LayoutSubviews在以下情况会调用
- 1.self setNeedsLayout
- 2.addSubview的时候。
- 3.当view的size发生改变的时候。
- 4.滑动UIScrollView的时候。
- 5.旋转Screen会触发父UIView上的layoutSubviews事件。
注意: 当view的size的值为0的时候,addSubview也不会调用layoutSubviews。当要给这个view添加子控件的时候不管他的size有没有值都会调用
- 3.UIButton设置title的时候会调用一次,设置image的时候会调用一次(image的名字工程里面必须有)
.外界有可能会调用setTitle 或者setImage方法,这个时候我们就需要封装一下
// 重写系统的方法一定要还原系统原有的方法,然后再增加自己的方法
- (void)setTitle:(NSString *)title forState:(UIControlState)state{
[super setTitle:title forState:state];
[self sizeToFit];
}
- (void)setImage:(UIImage *)image forState:(UIControlState)state{
[super setImage:image forState:state];
[self sizeToFit];
}
隐藏底部tabBar
- 1.隐藏系统的TabBar有两种方法
- 图形化界面
- 2.用代码,调用vc.hidesBottomBarWhenPushed
由于TabBar是我们自己写的所以不能隐藏(换第二种方案)
1.在viewWillApper里面移除tabBar的子控件
2.移除UITabBarButton(私有的控件)的3种方法
// 1.移除系统的TabBar中的所有子空间,添加自己的子控件
for (UIView *view in self.tabBar.subviews) {
// 1.将类名转换成字符串,对比前缀,如果前缀是以UITabBar开头的,我们就移除
NSString *classString = NSStringFromClass([view class]);
if ([classString hasPrefix:@"UITabBar"]) {
[view removeFromSuperview];
}
// 2.看一下这个类的父类是什么东西,子类敲不出来,看看父类能不能敲出来
NSLog(@"-- %@",view.superclass);
if ([view isKindOfClass:[UIColor class]]) {
[view removeFromSuperview];
}
// 3.判断是不是我们自己添加的类,如果不是自己添加的类就移除
if (![view isKindOfClass:[XXXTabBar class]]) {
[view removeFromSuperview];
}
}
统一设置返回按钮
- 1.NavController的内容由栈顶控制器决定,可以在对应的类里面实现
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:nil style:nil target:nil action:nil];
-
2.统一设置左侧返回按钮
- 1.在NavController拦截所有的push操作,才能是现实统一设置
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
// NSLog(@"%@",viewController);
if (self.childViewControllers.count > 0) { // 非跟控制
// 设置导航条左侧返回按钮
viewController.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithImage:[UIImage imageWithRenderingOriginalName:@"NavBack"] style:UIBarButtonItemStylePlain target:self action:@selector(back)];
}
[super pushViewController:viewController animated:animated];
}
实现滑动返回
- 1.如果自定义了导航条左侧的返回按钮就会出现不能滑动移除控制器的bug
- 2.如果要想既要定义返回按钮,又要实现滑动移除控制器需要将系统的手势代理清空
// 将系统的滑动移除手势代理清空就能实现滑动移除控制器iOS6不支持
self.interactivePopGestureRecognizer.delegate = nil;
-3.但是将系统的滑动移除手势清空当到达栈顶控制器的时候如果在向右滑动,就会出现bug,卡死
- 1.解决办法:
当不是跟控制器的时候就清空代理,如果是跟控制器的话就设置为原来的值 - 2.用nav的代理方法判断是否为跟控制器
// 控制器显示完毕的时候调用
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated{
if ([viewController isKindOfClass:[self.childViewControllers[0] class]]) { // 跟控制器
self.interactivePopGestureRecognizer.delegate = self.popGestureDelegate;
}else{ // 非根控制器
self.interactivePopGestureRecognizer.delegate = nil;
}
}
返回按钮的简单实现
1.实现起来简单
2.思想:让系统的文字移出视野,导航控制器设置不了,我们可以设置他的模型,是有模型决定的
// 设置导航条的主题色
[bar setTintColor:[UIColor whiteColor]];
// 调整导航条左侧返回按钮的标题位置,移除到屏幕外面
UIBarButtonItem *item = [UIBarButtonItem appearanceWhenContainedIn:self, nil];
[item setBackButtonTitlePositionAdjustment:UIOffsetMake(0, -64) forBarMetrics:UIBarMetricsDefault];
滑动移除控制器全屏实现
1.思想:系统已经做了从最左边滑动移除控制器
1.我们可以看一下系统有没有提供从中间滑动移除控制器的方法
2.需要自己添加手势
1.利用runtime取出系统的target名
// 1. 获取系统的手势
UIScreenEdgePanGestureRecognizer *gesture = self.interactivePopGestureRecognizer;
// 2. runtime只能动态获取当前类的所有属性,不能获取他的子类和父类的属性
// ivar 是属性
// Class是获取那个类的属性
// outCount 这个类下一共有多少个属性
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([UIGestureRecognizer class], &count);
NSLog(@"ivars %@",ivars);
for (int i = 0; i < count; i++) {
NSString *name = @( ivar_getName(ivars[i]));
NSLog(@"%@",name);
}
2.获取系统的target
NSArray *targets = [gesture valueForKeyPath:@"_targets"];
id gestureRecognizer = targets[0];
// 取到系统的target
id target = [gestureRecognizer valueForKeyPath:@"_target"];
3.添加全局手势
// 禁用系统的手势
self.interactivePopGestureRecognizer.enabled = NO;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
[self.view addGestureRecognizer:pan];
- 改进bug
UIScreenEdgePanGestureRecognizer *gest = self.interactivePopGestureRecognizer;
// 2.自己添加手势
// // 禁止系统的手势
id target = self.interactivePopGestureRecognizer.delegate;
self.interactivePopGestureRecognizer.enabled = NO;
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
pan.delegate = self;
新特性业务逻辑
程序一启动的时候就判断一下当前进入主框架界面,还是进入引导页界面
CFBundleShortVersionString 版本号保存在这里
// 1.获取当前版本号
NSString *currVersion = [NSBundle mainBundle].infoDictionary[@"CFBundleShortVersionString"];
// NSLog(@"%@",currVersion);
//
// // 2.上一次版本号
// NSString *lastVersion = [[NSUserDefaults standardUserDefaults] objectForKey:Version];
NSString *lastVersion = [SaveTool objectForKey:Version];
UIViewController *rootVc;
if (![currVersion isEqualToString:lastVersion]) {
// 进入新特界面
rootVc = [[NewFeatureCollectionViewController alloc] init];
// rootVc.view.backgroundColor = [UIColor yellowColor];
// 存储当前版本号
// [[NSUserDefaults standardUserDefaults] setObject:currVersion forKey:Version];
//
// [[NSUserDefaults standardUserDefaults] synchronize];
[SaveTool setObject:currVersion forKey:Version];
}else{
// 进入主框架
rootVc = [[TabBarViewController alloc] init];
}
initialize讲解
1.这个方法不一定会调用一次,比如说如果有很多子类,第一次使用子类的时候,这个方法会调用很多次
2.先调用父类,在调用子类
切换主框架界面
1.切换界面的的几种方式: 1.push 2.tabBarVc 3.modal
2.新特性界面只展示一次,所以我们没有必要让他一直占用着内存 (切换窗口的跟控制器)
//切换跟窗口
CPMainTabBarVC *rootVC = [[CPMainTabBarVC alloc] init];
[UIApplication sharedApplication].keyWindow.rootViewController = rootVC;
业务逻辑类
1.业务逻辑类是用于处理专门的业务逻辑的,后期如果业务逻辑改变的话,我们只需要改变业务逻辑类里面的实现方式就行了,不需要改动其他的地方
2.业务逻辑类的好处:
1.用于处理复杂的业务逻辑,
2.如果后期业务逻辑修改,直接找到对应的业务逻辑类,进行修改,
3.方便统一管理
搭建设置界面
1.push的时候隐藏tabBar,但是现在没有隐藏
// 隐藏底部工具条
vc.hidesBottomBarWhenPushed = YES;
2.重写nav的push方法,在非跟控制器的时候隐藏底部工具条
3.设置界面开发方式
1.Storyboard 静态单元格
1.1 我们的界面都不一样,不利于后期做延展
1.2 哪个地方用了静态单元格, 是不是Discover里面,为什么这个能用静态单元格,是不是因为我这个数据少的可怜,并且每个cell都长得一样,所以我才敢用静态单元格
2.纯代码
2.1界面比较多而且都是一样的,后期改动的可能性大,所以用纯代码搭建
4.搭建设置界面
5.用普通的方法
6.利用MVC创建行模型
@interface SettingItem : NSObject
/** 图片 */
@property (nonatomic, strong) UIImage *icon;
/** 标题 */
@property (nonatomic, copy) NSString *title;
/** 子标题 */
@property (nonatomic, copy) NSString *subTitle;
/** 点击这一行要做的事情 */
@property (nonatomic, copy) void(^operationBlock)(NSIndexPath *indexPath);
+ (instancetype)itemWithIcon:(UIImage *)icon title:(NSString *)title;
+ (instancetype)itemWithTitle:(NSString *)title;
@end
@interface CPSettingArrowItem : CPSettingItem
@property (nonatomic, assign) Class vc;
@end
#import <UIKit/UIKit.h>
#import "CPSettingItem.h"
#import "CPSettingSwitch.h"
#import "CPSettingArrowItem.h"
#import "CPSettingGroup.h"
NS_ASSUME_NONNULL_BEGIN
@interface CPSettingCell : UITableViewCell
@property (nonatomic, strong) CPSettingItem *item;
+(instancetype)cellWithTableView:(UITableView *)tableView;
+(instancetype)cellWithTableView:(UITableView *)tableView style:(UITableViewCellStyle )style;
@end
NS_ASSUME_NONNULL_END
block循环引用
1.循环引用就是你引用我,我引用你,大家都不会释放
2.解决方案就是打断其中的一个强指针,就能解决循环引用问题,但是我们只能打断block里面的强指针,其他的强指针均不能打断
__weak SettingTableViewController *weakSelf = self;
// 简写的方法
__weak typeof(self) weakSelf = self;
3.在block里面访问成员属性,也是会造成循环引用,成员属性最终转换成的代码 self -> _groups,只不过是苹果把他屏蔽了
封装基类,比分直播,cell键盘处理
1.封装基类
2.搭建比分直播界面
3.给模型添加subTitle
4.点击结束时间要弹出键盘,封装到block中
5.在iOS7以后,把textField添加到cell上,系统就会自动帮我们设置好键盘位置
6.滚动的时候把键盘退出在scrollView代理方法里实现
// 当开始滚动的时候调用
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView{
// 退出键盘
[self.view endEditing:YES];
}