Core Animation 3D介绍(第2部分)

尊重原创 转自:http://codingobjc.com/blog/2013/06/24/core-animation-3djie-shao-di-2bu-fen/

上一篇教程中,我们已经学习了Core
Animation中3D绘图的基本方法。这篇教程,我们准备深入一些,讲讲如何创建一个可交互的3D场景。我们将会创建一个类似于旋转木马的3D场景界面,用户可以通过拖拽手势来旋转界面。
可能你已经在上一篇教程中见过最终APP的样子了,但是,让我们再来看一次吧:

你可以直接下载教程的源代码。本篇教程的源代码和上一篇教程中的源代码在同一个代码仓库里面,但是我做了一些修改,为这篇教程建立了一个新的target。

###概述
这个程序,我们把工作分成了以下几个部分。

####3D
因为我们需要3D效果,所以我们需要使用一个透视视图来进行绘制工作。为了构造旋转木马效果,我们将会建立一个3D层次体系。正如前一篇教程中介绍过的,用CATransformLayer来做层次体系中的根是不错的选择。

####平面
旋转木马效果由一系列平面构成。我们将使用CAGradientLayer来表示这些平面。CAGradientLayer是CALayer的一个子类,用它可以制作渐变背景色,会比纯色背景要好看一些。
我们会对这些平面进行一些平移和旋转操作,使它们沿着旋转木马的圆周排列。

####手势
获取用户的手势非常简单。我们只需要使用一个手势识别器(Gesture Recognizer)就可以了。然后,我们追踪用户的动作,将手势数据转换成角度值,用这个角度值来旋转界面。
好了,现在你应该已经知道这个项目有哪些东西要完成了吧?启动XCode吧!

###写代码吧
展开项目中的TB_3DPlanes文件夹,然后打开ViewController.m文件。
我们从viewDidLoad这个函数开始:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

-(void)viewDidLoad{[superviewDidLoad];//Initialize the TransformLayertransformLayer=[CATransformLayerlayer];transformLayer.frame=self.view.bounds;[self.view.layeraddSublayer:transformLayer];angle=0;XPanOffset=0;//Create 5 planes[selfaddPlane];[selfaddPlane];[selfaddPlane];[selfaddPlane];[selfaddPlane];//Force the first animation to set the planes in place[selfanimate];//Initialize the Pan gesture recognizerUIPanGestureRecognizer*panGesture=[[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(pan:)];[self.viewaddGestureRecognizer:panGesture];}

或许这个函数中有许多东西你还不完全明白是什么意思。但是,确实从这里开始就是核心部分的代码了。你可以暂时不用在这里纠结,继续阅读,后面会解释这些代码的意思的。
很显然,我们先用CATransformLayer创建了一个根图层对象(transformLayer),然后我们用addPlane函数向场景中添加了5个平面,并且我们将用pan:函数来处理拖拽手势。

###绘制平面
addPlane函数的代码也是相当直观的。它只是用CAGradientLayer和一些参数创建了一个图层罢了。然后将图层添加到了transformLayer上.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

/** A simple function to create a CAGradientLayer **/-(void)addPlane{CGSizeplaneSize=CGSizeMake(250,150);//Initialize the layerCAGradientLayer*layer=[CAGradientLayerlayer];//Set the frame and the anchorPointlayer.frame=CGRectMake(480/2-planeSize.width/2,320/2-planeSize.height/2-20,planeSize.width,planeSize.height);layer.anchorPoint=CGPointMake(0.5,0.5);//Set borders and cornerRadiuslayer.borderColor=[[UIColorcolorWithWhite:1.0alpha:0.3]CGColor];layer.cornerRadius=10;layer.borderWidth=4;//Set the gradient color for the plane backgroundlayer.colors=[NSArrayarrayWithObjects:(id)[UIColorpurpleColor].CGColor,(id)[UIColorredColor].CGColor,nil];layer.locations=[NSArrayarrayWithObjects:[NSNumbernumberWithFloat:0.0f],[NSNumbernumberWithFloat:1.0f],nil];//Set the shadowlayer.shadowColor=[[UIColorblackColor]CGColor];layer.shadowOpacity=1;layer.shadowRadius=20;//The double side has to be setted if we want to see the plane when its face is turned backlayer.doubleSided=YES;//Add the plane to the transformLayer[transformLayeraddSublayer:layer];}

这里唯一需要解释一下的是doubleSide属性。将它设置成YES,表示我们希望图层的背面也被绘制出来。当一个平面在Y轴上旋转接近180度的时候,我们仍然可以在场景中看到它,只是它旋转到了相反的方向。
为了更形象地说明这个问题,可以看看下图doubleSide为NO和YES时的对比。

看了图,一下就明白了吧?设置成NO的时候,被旋转到背对着我们的那些平面都不会被绘制出来。
也许你已经注意到了,在viewDidLoad函数中我们添加了5个平面,但是这些平面并不是按位置排列在场景中的。在addPlane函数中,它们被添加到了同一个位置(参见frame属性)。

###更新平面位置
在viewDidLoad函数里,平面被创建后,我们调用了animate方法。这个函数的主要作用是更新各个平面的位置。这里是我们第一次调用这个函数,这个时候并没有发生任何触摸事件,这里调用它只是为了让各个平面沿着旋转木马的圆周排列好。
我们来看看这个函数的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

/** This function performs the transformation on each plane **/-(void)animate{//Define the degrees needed for each plane to create a circlefloatdegForPlane=360/[[transformLayersublayers]count];//The current angle offset (initially it is 0... it will change through the pan function)floatdegX=angle;for(CALayer*layerin[transformLayersublayers]){//Create the Matrix identityCATransform3Dt=CATransform3DIdentity;//Setup the perspective modifying the matrix elementat [3][4]t.m34=1.0f/-1000.0f;//Perform rotate on the matrix identityt=CATransform3DRotate(t,degToRad(degX),0.0f,1.0f,0.0f);//Perform translate on the current transform matrix (identity + rotate)t=CATransform3DTranslate(t,0.0f,0.0f,250.0f);//Avoid animations[CATransactionsetAnimationDuration:0.0];//apply the transoform on the current layerlayer.transform=t;//Add the degree needed for the next planedegX+=degForPlane;}}

其中,defForPlane变量是指沿着360度圆周上每个平面需要旋转的角度。看下图会容易理解一些:

圆周上的5个平面需要旋转一定的角度。这个角度从0开始,每个平面增加”360/平面总数”度。
angle变量的值目前还是零,我们稍后会详细解释它的作用。
animate函数循环遍历transformLayer的所有子图层(5个平面),为每一个平面加上一些变换效果:
第一个变换效果是改变视点,和我们在上一篇教程中的做法一样,直接给m34属性赋一个值,从而可以绘制出平面的3D空间深度。
另外2个变换效果需要一点点数值计算。我们前面已经谈到了,每个平面需要一个旋转角度。但是我们跳过了一个关键点,还没讲。
如果我们只是给每个平面加上一个旋转角度,我们只会得到下面这种效果:

你可以看到,这种效果不行,我们还需要给每个平面加上平移变换,使让它们沿着圆周排列。
最后一步是将这些变换应用到各个平面上。只需要将变换赋值给当前平面的transform属性就可以了,赋值后,下一个平面的degX值会增加”degForPlane”度。

###拖拽手势
pan:函数是拖拽手势的处理函数。下面是该函数的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

-(void)pan:(UIPanGestureRecognizer*)gesture{//Get the current translation on the XfloatxOffset=[gesturetranslationInView:self.view].x;//When gesture begin, reset the offsetif(gesture.state==UIGestureRecognizerStateBegan){XPanOffset=0;}//the distance covered since the last gesture event (I slow down a bit the final rotation multiplying by 0.5)floatmovedBy=xOffset*0.5-XPanOffset;//Calculate the offset from the previous gesture eventXPanOffset+=movedBy;//Add the offset to the current angleangle+=movedBy;//Update the plane[selfanimate];}

拖拽手势是一个连续性的手势。从手势开始,直到所有手指离开屏幕之前,只要手指一移动,pan函数就会被调用。
为了将这些手势数据转换成对旋转木马动画有用的数据,我们需要获得每次移动前的X坐标值。
这里我们使用了XPanOffset这个变量来存储位置信息。当手势发生移动后,我们会计算出本次移动的距离(movedBy变量)。这个移动距离会被加到XPanOffset中,也会被加到当前的angle变量上。
然后调用animate函数就可以将所有平面旋转到新角度。
就这样,完成了!
这个简单的例子展示了只需用Core Animation就可以做出来的一些3D效果。
祝你玩的愉快!有任何疑问或建议,请随时在twitter上联系我。
教程源代码下载
译自:Think
& Build

MessageUI.framework发短信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#import "ViewController.h"
#import <MessageUI/MessageUI.h>
@interface ViewController ()<MFMessageComposeViewControllerDelegate>

@end

@implementation ViewController

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.



UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom];
[self.view addSubview:b];
b.frame = CGRectMake(100, 100, 200, 100);
b.backgroundColor = [UIColor blackColor];
[b setTitle:@"sendMessage" forState:UIControlStateNormal];
[b addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];

}
-(void)buttonAction
{

if (![MFMessageComposeViewController canSendText])
{
NSLog(@"当前设备不能发短信");
return;

}else
{

MFMessageComposeViewController *messageVC = [[MFMessageComposeViewController alloc]init];

messageVC.recipients = @[@"10086",@"10010"];
messageVC.body = @"大水杯";
messageVC.messageComposeDelegate = self;

[self presentViewController:messageVC animated:YES completion:^{


}];
}
}

-(void)messageComposeViewController:(MFMessageComposeViewController *)controller didFinishWithResult:(MessageComposeResult)result
{

[self dismissViewControllerAnimated:YES completion:^{


}];

}

- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}

@end

MPMoviePlayerViewController和MPMoviePlayerController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (void)viewDidLoad {
[super viewDidLoad];

[self playVideo:[NSURL URLWithString:self.playURL]];

}
//根据视频url播放视频
- (void) playVideo:(NSURL *) movieURL
{
MPMoviePlayerViewController *playerViewController = [[MPMoviePlayerViewController alloc] initWithContentURL:movieURL];
self.playerViewController = playerViewController;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playVideoFinished:) name:MPMoviePlayerPlaybackDidFinishNotification object:[playerViewController moviePlayer]];
playerViewController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self.view addSubview:playerViewController.view];
MPMoviePlayerController *player = [playerViewController moviePlayer];
[player play];
}

//当点击Done按键或者播放完毕时调用此函数
- (void) playVideoFinished:(NSNotification *)theNotification
{
MPMoviePlayerController *player = [theNotification object];
[[NSNotificationCenter defaultCenter] removeObserver:self name:MPMoviePlayerPlaybackDidFinishNotification object:player];
[player stop];

[self dismissViewControllerAnimated:YES completion:^{


}];
}

MPMoviePlayerViewController和MPMoviePlayerController 使用场合不一样MPMoviePlayerViewController是在iOS3.2以后的平台上使用。MPMoviePlayerController在3.2之前使用,虽然在3.2之后也能使用,但是使用方法略有改变,建议3.2之后使用MPMoviePlayerViewController。
3.2之后,MPMoviePlayerController作为MPMoviePlayerViewController的一个属性存在。

以下是使用MPMoviePlayerViewController播放视频的代码:

去掉系统上的按钮导航条 self.play.controlStyle = MPMovieControlStyleNone;

NSHTTPCookieStorage

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//保存cookie
+ (void)saveCookies {
NSData *cookiesData = [NSKeyedArchiver archivedDataWithRootObject: [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject: cookiesData forKey:kCookie];
[defaults synchronize];
}

//加载cookie
+ (void)loadCookies {

NSArray *cookies = [NSKeyedUnarchiver unarchiveObjectWithData: [[NSUserDefaults standardUserDefaults] objectForKey: kCookie]];
NSHTTPCookieStorage *cookieStorage = [NSHTTPCookieStorage sharedHTTPCookieStorage];

for (NSHTTPCookie *cookie in cookies){
[cookieStorage setCookie: cookie];
}

}

//删除cookie
+ (void)clearCookies {

NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies];
for (NSHTTPCookie *cookie in cookies)
{
[[NSHTTPCookieStorage sharedHTTPCookieStorage] deleteCookie:cookie];
}
}

Masonry介绍与使用实践:快速上手Autolayout

转自:http://www.cocoachina.com/ios/20141219/10702.html
前言
MagicNumber -> autoresizingMask -> autolayou
以上是纯手写代码所经历的关于页面布局的三个时期
在iphone1-iphone3gs时代 window的size固定为(320,480) 我们只需要简单计算一下相对位置就好了
在iphone4-iphone4s时代 苹果推出了retina屏 但是给了码农们非常大的福利:window的size不变
在iphone5-iphone5s时代 window的size变了(320,568) 这时autoresizingMask派上了用场(为啥这时候不用Autolayout? 因为还要支持ios5呗) 简单的适配一下即可
在iphone6+时代 window的width也发生了变化(相对5和5s的屏幕比例没有变化) 终于是时候抛弃autoresizingMask改用autolayout了(不用支持ios5了 相对于屏幕适配的多样性来说autoresizingMask也已经过时了)
那如何快速的上手autolayout呢? 说实话 当年ios6推出的同时新增了autolayout的特性 我看了一下官方文档和demo 就立马抛弃到一边了 因为实在过于的繁琐和啰嗦(有过经验的朋友肯定有同感)
直到iPhone6发布之后 我知道使用autolayout势在必行了 这时想起了以前在浏览Github看到过的一个第三方库Masonry 在花了几个小时的研究使用后 我就将autolayout掌握了(重点是我并没有学习任何的官方文档或者其他的关于autolayout的知识) 这就是我为什么要写下这篇文章来推荐它的原因.
介绍
Masonry 源码:https://github.com/Masonry/Masonry
Masonry是一个轻量级的布局框架 拥有自己的描述语法 采用更优雅的链式语法封装自动布局 简洁明了 并具有高可读性 而且同时支持 iOS 和 Max OS X。
我们先来看一段官方的sample code来认识一下Masonry
[view1 mas_makeConstraints:^(MASConstraintMaker make) {
make.edges.equalTo(superview).with.insets(padding);
}];
看到block里面的那句话: make edges equalTo superview with insets
通过链式的自然语言 就把view1给autolayout好了 是不是简单易懂?
使用
看一下Masonry支持哪一些属性
@property (nonatomic, strong, readonly) MASConstraint
left;
@property (nonatomic, strong, readonly) MASConstraint top;
@property (nonatomic, strong, readonly) MASConstraint
right;
@property (nonatomic, strong, readonly) MASConstraint bottom;
@property (nonatomic, strong, readonly) MASConstraint
leading;
@property (nonatomic, strong, readonly) MASConstraint trailing;
@property (nonatomic, strong, readonly) MASConstraint
width;
@property (nonatomic, strong, readonly) MASConstraint height;
@property (nonatomic, strong, readonly) MASConstraint
centerX;
@property (nonatomic, strong, readonly) MASConstraint centerY;
@property (nonatomic, strong, readonly) MASConstraint
baseline;
这些属性与NSLayoutAttrubute的对照表如下

其中leading与left trailing与right 在正常情况下是等价的 但是当一些布局是从右至左时(比如阿拉伯文?没有类似的经验) 则会对调 换句话说就是基本可以不理不用 用left和right就好了
在ios8发布后 又新增了一堆奇奇怪怪的属性(有兴趣的朋友可以去瞅瞅) Masonry暂时还不支持(不过你要支持ios6,ios7 就没必要去管那么多了)
在讲实例之前 先介绍一个MACRO
1#define WS(weakSelf) __weak __typeof(&*self)weakSelf = self;快速的定义一个weakSelf 当然是用于block里面啦 下面进入正题(为了方便 我们测试的superView都是一个size为(300,300)的UIView)
下面 通过一些简单的实例来简单介绍如何轻松愉快的使用Masonry:

  1. [基础] 居中显示一个view
    1234567891011121314151617- (void)viewDidLoad`{[super` `viewDidLoad]; // Do any additional setup after loading the view. WS(ws); UIView *sv = [UIViewnew]; [sv showPlaceHolder]; sv.backgroundColor = [UIColor blackColor]; [self.view addSubview:sv]; [sv mas_makeConstraints:^(MASConstraintMaker *make) { make.center.equalTo(ws.view); make.size.mas_equalTo(CGSizeMake(300, 300)); }]; }`

NSUserDefaults

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*NSUserDefaults通常用于存储用户偏好设置和登陆注册等信息*/

//取值
[[NSUserDefaults standardUserDefaults]objectForKey:@"night"];
//存值
[[NSUserDefaults standardUserDefaults] setObject:@"yes" forKey:@"night"];


//存储数据
NSUserDefaults *userDefault = [NSUserDefaults standardUserDefaults];

[userDefault setObject:@"张三" forKey:@"name"];
[userDefault setInteger:23 forKey:@"age"];

//取出数据
self.navigationItem.title = [[NSUserDefaults standardUserDefaults]objectForKey:@"name"];
NSLog(@"%ld",[[NSUserDefaults standardUserDefaults]integerForKey:@"age" ]);

//修改数据
[[NSUserDefaults standardUserDefaults]setObject:@"李四" forKey:@"name"];

//强制存储
/*原因:NSUserDefaults存储数据会发生延迟现象,把数据存储到本地文件中,不是马上就生效,所以会出现娶不到数据现象*/
[[NSUserDefaults standardUserDefaults]synchronize];

OC 函数库方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  2.1.1 rand()函数:产生随机数 
  2.1.2 abs()函数/labs()函数:整数的绝对值 
  2.1.3 fabs()/fabsf()/fabsl()函数:浮点数的绝对值 
  2.1.4 floor()/floorf()/floorl()函数:向下取整 
  2.1.5 ceil()/ceilf()/ceill()函数:向上取整 
  2.1.6 round()/roundf()/roundl()函数:四舍五入 
  2.1.7 sqrt()/sqrtf()/sqrtl()函数:求平方根 
  2.1.8 fmax()/fmaxf()/fmaxl()函数:求最大值 
  2.1.9 fmin()/fminf()/fminl()函数:求最小值 
  2.1.10 hypot()/hypotf()/hypotl()函数:求直角三角形斜边的长度 
  2.1.11 fmod()/fmodf()/fmodl()函数:求两数整除后的余数 
  2.1.12 modf()/modff()/modfl()函数:浮点数分解为整数和小数 
  2.1.13 frexp()/frexpf()/frexpl()函数:浮点数分解尾数和以二为底的指数 
  2.2 三角运算函数 
  2.2.1 sin()/sinf()/sinl()/函数:求正弦值 
  2.2.2 sinh()/sinhf()/sinhl()函数:求双曲正弦值 
  2.2.3 cos()/cosf()/cosl()函数:求余弦值 
  2.2.4 cosh()/coshf()/coshl()函数:求双曲余弦值 
  2.2.5 tan()/tanf()/tanl()函数:求正切值 
  2.2.6 tanh()/tanhf()/tanhl()函数:求双曲正切值 
  2.3 反三角运算 
  2.3.1 asin()/asinf()/asinl()函数:求反正弦值 
  2.3.2 asinh()/asinhf()/asinhl()函数:求反双曲正弦值 
  2.3.3 acos()/acosf()/acosl()函数:求反余弦值 
  2.3.4 acosh()/acoshf()/acoshl()函数:求反双曲余弦值 
  2.3.5 atan()/atanf()/atanl()函数:求反正切值 
  2.3.6 atan2()/atan2f()/atanl()函数:求坐标值的反正切值 
  2.3.7 atanh()/atanhf()/atanhl()函数:求反双曲正切值 
  2.4 指数和对数运算 
  2.4.1 pow()/powf()/powl函数:求n的m次方的值 
  2.4.2 exp()/expf()/expl()函数:求e的x次方的值 
  2.4.3 exp2()/exp2f()/exp2l()函数:2的x的次方的值 
  2.4.4 log()/logf()/logl()函数:求以e为底的对数值 
  2.4.5 log10()/log10f()/log10l()函数:求以10为底的对数值

UIAlertController

//初始化方法

+ (instancetype)alertControllerWithTitle:(nullableNSString *)title message:(nullableNSString *)message preferredStyle:(UIAlertControllerStyle)preferredStyle;

//UIAlertControllerStyle
typedefNS_ENUM(NSInteger, UIAlertControllerStyle) {
UIAlertControllerStyleActionSheet =0, UIAlertControllerStyleAlert }
/**自动消失的UIAlertController*/ //创建UIAlertController
UIAlertController*alertController = [UIAlertControlleralertControllerWithTitle:@”标题”message:@”UIAlertController”preferredStyle:UIAlertControllerStyleAlert];

[selfpresentViewController:alertControlleranimated:YEScompletion:nil];

//线程2秒后执行
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
(int64_t)(2.f*NSEC_PER_SEC)),dispatch_get_main_queue(), ^{

    [alertControllerdismissViewControllerAnimated:YEScompletion:^{


    }];
});   /**************自动消失的UIAlertController*********/ 

/
**添加按钮**
Title :标题名称
style :样式[Cancle(取消)
Default(默认的) destructive(重置)]
handler:处理程序(点击按钮执行的代码)
/

/**带按钮的的UIAlertController*/
//创建UIAlertController
UIAlertController*alertController = [UIAlertControlleralertControllerWithTitle:@”标题”message:@”UIAlertController”preferredStyle:UIAlertControllerStyleAlert];

//创建”取消”样式按钮
UIAlertActioncancelAction = [UIAlertActionactionWithTitle:@”取消”style:UIAlertActionStyleCancelhandler:^(UIAlertActionaction) {
//添加点击事件
self.view.backgroundColor= [UIColoryellowColor];

}];

//创建”默认”样式按钮
UIAlertActiondefaultAction = [UIAlertActionactionWithTitle:@”默认default”style:UIAlertActionStyleDefaulthandler:^(UIAlertAction_Nonnullaction) {

}];

//创建“警示”样式按钮
UIAlertActiondestructiveAction = [UIAlertActionactionWithTitle:@”重置deatructive” style:UIAlertActionStyleDestructivehandler:^(UIAlertAction_Nonnullaction) {

}];

//将按钮添加到alertController上
[alertControlleraddAction:cancelAction];
[alertControlleraddAction:defaultAction];
[alertControlleraddAction:destructiveAction];

[selfpresentViewController:alertControlleranimated:YEScompletion:nil];

/**带按钮的的UIAlertController*/

/**带输入框的UIAlertController*/ //创建UIAlertController
UIAlertController*alertController = [UIAlertControlleralertControllerWithTitle:@”文本对话框”message:@”登录和密码对话框示例”preferredStyle:UIAlertControllerStyleAlert];

//添加输入框
[alertControlleraddTextFieldWithConfigurationHandler:^(UITextField*textField){
textField.placeholder=@”登录”;

   //添加通知,监听输入框的变化
    [[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(alertTextFieldDidChange:)name:UITextFieldTextDidChangeNotificationobject:textField];
}];

[alertControlleraddTextFieldWithConfigurationHandler:^(UITextField*textField) {
    textField.placeholder=@"密码";
    textField.secureTextEntry=YES;
}];

//添加按钮
UIAlertActionokAction = [UIAlertActionactionWithTitle:@”好的”style:UIAlertActionStyleDefaulthandler:^(UIAlertActionaction) {

   //获取到输入框的内容
   UITextField*login = alertController.textFields.firstObject;
   UITextField*password = alertController.textFields.lastObject;

   //移除通知
    [[NSNotificationCenterdefaultCenter]removeObserver:selfname:UITextFieldTextDidChangeNotificationobject:nil];

}];

//冻结按钮
okAction.enabled=NO;
[alertControlleraddAction:okAction];

[selfpresentViewController:alertControlleranimated:YEScompletion:^{


}];

/**带输入框的UIAlertController*/
}
//通知触发的方法

  • (void)alertTextFieldDidChange:(NSNotification)notification{
    UIAlertController
    alertController = (UIAlertController*)self.presentedViewController;

    if(alertController) {

    UITextField*login = alertController.textFields.firstObject;
    UIAlertAction*okAction = alertController.actions.lastObject;
    
    //当输入的字数大于2时解冻
     okAction.enabled= login.text.length>2;
    

    }
    }

|