这节课主要讲四种特性:Modal View Controllers、UITextField and UITextView、UIView Animation和NSTimer。

Modal View Controllers

它是一个模式,当用户要继续做某些事情的时候必须先做别的事情,例如以下app:

如何使Modal View Controllers出现在屏幕上?用segue,从一个button或bar button或什么的control-drag出来,来触发Modal View Controllers。点击之后,在xcode中control-drag以创建一个segue,然后将其设置为modal,当你inspect这个segue,在这里可以设置类似它的出现方式之类的东西。

也可以从代码中放上Modal View Controllers,方法是:在xcode中创建view controller,但不是control-drag到这里,而是只要给它取个名字。记住,所有的view controller都有一个标识符字段。然后就可以创建view controller了。

- (IBAction)lookupAddress {
    AddressLookupViewController *alvc = [self.storyboard instantiateViewControllerWithIdentifier:@“AddressLookup”];
    [self presentModalViewController:alvc animated:YES completion:^{
        // alvc is now on screen; often we do nothing here
    }];
}

一旦有想要呈现的东西,只需要调用此方法。这边还有个completion block,当Modal View Controllers完成其动画之后会被调用。调用presentModalViewController或segue到它,屏幕将被这个Modal View Controllers完全充满,此时你的view controller无法做任何事情,它没有屏幕的访问权。

它什么时候算是全部结束呢?把Modal View Controllers放上屏幕的时候,app正在等,不能做任何事,除了Modal View Controllers想要做的。直到这个view controller调用dismissModalViewControllerAnimated,它才会结束。

- (void)dismissModalViewControllerAnimated:(BOOL)animated;

不是给被显示的view controller发送dismissModalViewControllerAnimated,而应该是给那个把Modal View Controllers放上来的对象发。

Modal View Controllers可以dismissModalViewControllerAnimated自己,所有view controller都有个方法称为presentingViewController,也就是说,先找到我的presentingViewController,并让它dismissModalViewControllerAnimated我。

[[self presentingViewController] dismissModalViewControllerAnimated:YES];

但这个做法一般是不可取的。因为把Modal View Controllers放上来是为了从用户那得到一些信息,通常present你的view controller会想要知道发生了什么事,所以你会用Modal View Controllers的delegate来告诉presenter这里发生了什么事。它将自己设置为你的delegate,你只要告诉你的delegate就好了。

然而有时只需点击取消,就没什么好反馈的,即使在这种情况下,可能还是要用一个委托方法。只需要让presentingViewController去dismissModalViewControllerAnimated你,不用任何delegate。如果Modal View Controllers什么也没做,它可能在取消的情况下没问题。但是,如果你的Modal View Controllers做了一些事情,那么你需要告诉它发生了什么事,你需要使用delegation来告诉。你将需要写自己的委托方法,这里是做delegation的一个例子:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
       if ([segue.identifier isEqualToString:@“Lookup Address”]) {   
              AddressLookupViewController *alvc = (AddressLookupViewController *)segue.dest...; 
              // other setup here 
              alvc.delegate = self;
       }
}
// (One of) AddressLookupViewController’s delegate method(s) implemented in presenter ... 
- (void)addressLookupViewController:(AddressLookupViewController *)sender
                   didSelectAddress:(Address *)anAddress
{
        // do something with the address the user selected (anAddress)
        [selfdismissModalViewControllerAnimated:YES]; //takes sender off screen
}

这是从segue出来的Modal View Controllers,得到了正在显示的AddressLookupViewController,然后设置它。注意这里的“alvc.delegate = self;”,在prepareForSegue里我把自己设为它的delegate。下面是它的delegate方法,发送addressLookupViewController:didSelectAddress给我。这就是如果它不取消的情况下,该去dismiss的时候了。

Modal View Controllers是如何在屏幕上显示的?除了从底部滑出,还有其他的方式。

@property UIModalTransitionStyle modalTransitionStyle; 
UIModalTransitionStyleCoverVertical  //slides up and down from bottom of screen 
UIModalTransitionStyleFlipHorizontal // flips the current view controller view over to modal 
UIModalTransitionStyleCrossDissolve  //old fades out as new fades in 
UIModalTransitionStylePartialCurl    // only if presenter is full screen (and no more modal)

在iPad上还有另一种方式,在iphone上Modal View Controllers始终覆盖整个屏幕,但在ipad上是没有必要的。

@property UIModalPresentationStyle modalPresentationStyle; // in the modal VC
UIModalPresentationFullScreen      // full screen anyway (always on iPhone/iPod Touch) 
UIModalPresentationPageSheet       // full screen height, but portrait width even if landscape 
UIModalPresentationFormSheet       // centered on the screen (all else dimmed)
UIModalPresentationCurrentContext  // parent’s context (e.g. in a popover)

UITextField

键盘出现的方式是当一个TextField或TextView成为first responder,first responder意味这个view里的这个闪烁的文本插入符。当用户点击键盘,字符会出现在闪烁的插入符的位置,所以当任何TextField或TextView成为first responder时,其光标开始闪烁,键盘就会出现。

可以通过代码强制键盘消失,只要发送resignFirstResponder给这个闪烁的插入符。基本上,如果闪烁的插入符消失,键盘消失,键盘只在闪烁的插入符在的时候才出现。

TextField是用来编辑的,通过点击键盘上的return,或在代码里dismiss键盘,它不再是first responder。要如何取出它的文字?可以在用户在TextField里输入每个字符的时候获得文本,它有delegate方法,从TextField获得文本的方式是使用其delegate,这个delegate就是用来取出文本的。有两种主要的方法:

- (BOOL)textFieldShouldReturn:(UITextField*)sender; //sent when return key is pressed 
- (void)textFieldDidEndEditing:(UITextField *)sender;

textFieldDidEndEditing是插入符消失时发送到delegate的,用户要让键盘消失,或他们点击了return,这通常就是提取到了文本并要用它做什么的时候。textFieldShouldReturn返回布尔值,表示现在是否可以点return,这允许对文本字段中的内容进行验证。

插入符出现时键盘就会出现,这样就可以控制键盘的外观了,TextField实现UITextInputTraits这个protocol。以下这些都是通过UITextInputTraits控制的,也就是UITextField的property:

@property UITextAutocapitalizationType autocapitalizationType; // words, sentences, etc. 
@property UITextAutocorrectionType autocorrectionType;  // UITextAutocorrectionTypeYES/NO
@property UIReturnKeyType returnKeyType;                // Go, Search, Google, Done, etc. 
@property BOOL secureTextEntry;                         // for passwords, for example
@property UIKeyboardType keyboardType;                  // ASCII, URL, PhonePad, etc.

要注意的是,键盘从底部出来,它覆盖了view。得确保插入符引起的键盘出来后,TextField仍然是可见的。那么怎么做呢?必须注册这些通知,例如UIKeyboardWillShowNotification、UIKeyboardDidHideNotification等。

UITextView

UITextView和UITextField之间的区别是什么?UITextView和UITextField一样是多行、可滚动、可编辑的,它更多用来显示大段文字,UITextField更像是一个可编辑的label,UITextField是用来显示很短的信息。当你点击UITextFiel的return,通常是 resignFirstResponder;而当你点击UITextView的return,它会去到下一行。

UITextViewDelegate类似于UITextField的,编辑开始和结束还有内容变化的时候,你会得到通知并做出反应。

还有一个特殊的ScrollView方法,UITextView是个UIScrollView,它有个方法scrollRangeToVisible,你指定文字NSRange的后,它会自动滚动到那部分。

- (void)scrollRangeToVisible:(NSRange)rangeOfCharactersToScrollToVisible;

UIView Animation

UIView Animation允许你设置UIView的某些属性,比如frame、是否隐藏、transform是关于缩放旋转的、还有opacity,虽然它马上就设置好了这些东西,但它实际上要过段时间才会出现在屏幕上,它在屏幕上会有动画。视觉效果发生在另一个线程,它不会阻塞主线程。

完全透明的东西就隐藏了,是不能互动的。

动画要怎么做?这都是通过UIView类方法和block。UIView类方法不是实例方法,工作原理是:这个类方法,需要一个block,在该block内,可以改变任何view的参数。所有这些变化会立即生效,但动画要一个过程。还有方法可以设定持续时间、延迟开始时间。动画结束的时候执行另一个block,

Core Animation是一个动画地改变property的机制。

下面是一个最主要的方法:

+ (void)animateWithDuration:(NSTimeInterval)duration
                      delay:(NSTimeInterval)delay 
                    options:(UIViewAnimationOptions)options
                 animations:(void (^)(void))animations 
                 completion:(void (^)(BOOL finished))completion;

duration就是动画的持续时间;delay是先延迟一定时间然后再开始动画;animations,这是个block,可以在里面改变任何你想改的属性;completion,这将告诉你动画是否被中断或动画是否完成。

这边有一个变化透明度的例子:

[UIView animateWithDuration:3.0 
delay:0.0 options:UIViewAnimationOptionBeginFromCurrentState
animations:
^{ myView.alpha = 0.0; }
completion:^(BOOL fin) { if (fin) [myView removeFromSuperview]; }];

这会让myView用3秒时间从完全不透明淡出到透明。如果在3秒内有人把alpha改成了非0,那么这个finished会是false。这是一种将view淡出后移除的方法,但如果淡出过程中别人设置了alpha为非0,那么就无法完成,因此它不会被移除。下面是一个2秒后再启动动画的例子:

if (myView.alpha == 1.0) {
    [UIView animateWithDuration:3.0 
delay:2.0 options:UIViewAnimationOptionBeginFromCurrentState
animations:
^{ myView.alpha = 0.0; }
completion:nil]; NSLog(@“alpha
is %f.”, myView.alpha); }

输出的alpha值会是0,因为animation block是立即执行的,它不是异步执行。它的外观会动画,但实际的改变是立即发生的,所以alpha会是0。

如果你的动画会改变view的层级关系,那么要使用不同的方法,就是transitionFromView或transitionWithView:

+ (void)transitionFromView:(UIView *)fromView 
                    toView:(UIView *)toView
                  duration:(NSTimeInterval)duration 
                   options:(UIViewAnimationOptions)options
                completion:(void (^)(BOOL finished))completion;

fromView已经是在view层级里了,把它取出并把toView放进去。动画就是删除fromView,加入toView。

+ (void)transitionWithView:(UIView *)view 
                  duration:(NSTimeInterval)duration
                   options:(UIViewAnimationOptions)options 
                animations:(void (^)(void))animations 
                completion:(void (^)(BOOL finished))completion;

transitionWithView就是要把它的某些subViews隐藏,或别的animation block里的指令。

NSTimer

只需要调用scheduledTimerWithTimeInterval这个方法:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:(NSTimeInterval)seconds 
target:self selector:@selector(doSomething:)
userInfo:(
id)anyObject repeats:(BOOL)yesOrNo];

这就是从现在开始过多少秒后它要执行这一次,调用target的selector或者选择repeat为yes,那么这就是循环调用的间隔时间。userInfo是你想传递的任何信息。

这不是实时的,这仅仅是把东西放到main queue上,把它看成是那将一个会调用该方法block被放到了main queue上,但它会等一定的时间间隔。不要在timer上做很多重型工作,因为你不能阻塞main queue,它不适合做这种频繁程度的事情。

一定要在view离开屏幕的时候让timer无效。这个doSomething方法,它唯一的参数是timer,因此doSomething被调用时timer会被传递回来。

Demo

在xcode中创建一个新的项目,叫做Kitchen Sink。

UIView有一个选项,叫Clip subviews,如果打开这个,这意味着如果有一个subview部分超出了边界,会把它剪切掉。

AskerViewController.h文件代码:

#import <UIKit/UIKit.h>

@class AskerViewController;

@protocol AskerViewControllerDelegate <NSObject>
- (void)askerViewController:(AskerViewController *)sender
             didAskQuestion:(NSString *)question
               andGotAnswer:(NSString *)answer;
@end

@interface AskerViewController : UIViewController

@property (nonatomic, copy) NSString *question;
@property (nonatomic, copy) NSString *answer;

@property (nonatomic, weak) id <AskerViewControllerDelegate> delegate;

@end

AskerViewController.m文件代码:

#import "AskerViewController.h"

@interface AskerViewController() <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UILabel *questionLabel;
@property (weak, nonatomic) IBOutlet UITextField *answerTextField;
@end

@implementation AskerViewController
@synthesize questionLabel = _questionLabel;
@synthesize answerTextField = _answerTextField;

@synthesize question = _question;
@synthesize answer = _answer;

@synthesize delegate = _delegate;

- (void)setQuestion:(NSString *)question
{
    _question = question;
    self.questionLabel.text = question;
}

- (void)setAnswer:(NSString *)answer
{
    _answer = answer;
    self.answerTextField.placeholder = answer;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.questionLabel.text = self.question;
    self.answerTextField.placeholder = self.answer;
    self.answerTextField.delegate = self;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    [self.answerTextField becomeFirstResponder];
}

- (void)textFieldDidEndEditing:(UITextField *)textField
{
    self.answer = textField.text;
    if (![textField.text length]) {
        [[self presentingViewController] dismissModalViewControllerAnimated:YES];
    } else {
        [self.delegate askerViewController:self didAskQuestion:self.question andGotAnswer:self.answer];
    }
}

- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
    if ([textField.text length]) {
        [textField resignFirstResponder];
        return YES;
    } else {
        return NO;
    }
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

- (void)viewDidUnload
{
    [self setQuestionLabel:nil];
    [self setAnswerTextField:nil];
    [super viewDidUnload];
}

@end

KitchenSinkViewController.h文件代码:

#import <UIKit/UIKit.h>

@interface KitchenSinkViewController : UIViewController

@end

KitchenSinkViewController.m文件代码:

#import "KitchenSinkViewController.h"
#import "AskerViewController.h"

@interface KitchenSinkViewController() <AskerViewControllerDelegate>
@property (weak, nonatomic) IBOutlet UIView *kitchenSink;
@end

@implementation KitchenSinkViewController

@synthesize kitchenSink = _kitchenSink;

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier hasPrefix:@"Create Label"]) {
        AskerViewController *asker = (AskerViewController *)segue.destinationViewController;
        asker.question = @"What do you want your label to say?";
        asker.answer = @"Label Text";
        asker.delegate = self;
    }
}

- (void)setRandomLocationForView:(UIView *)view
{
    [view sizeToFit];
    CGRect sinkBounds = CGRectInset(self.kitchenSink.bounds, view.frame.size.width/2, view.frame.size.height/2);
    CGFloat x = arc4random() % (int)sinkBounds.size.width + view.frame.size.width/2;
    CGFloat y = arc4random() % (int)sinkBounds.size.height + view.frame.size.height/2;
    view.center = CGPointMake(x, y);
}

- (IBAction)tap:(UITapGestureRecognizer *)gesture
{
    CGPoint tapLocation = [gesture locationInView:self.kitchenSink];
    for (UIView *view in self.kitchenSink.subviews) {
        if (CGRectContainsPoint(view.frame, tapLocation)) {
            [UIView animateWithDuration:4.0 animations:^{
                [self setRandomLocationForView:view];
            }];
        }
    }
}

- (void)addLabel:(NSString *)text
{
    UILabel *label = [[UILabel alloc] init];
    label.text = text;
    label.font = [UIFont systemFontOfSize:48.0];
    label.backgroundColor = [UIColor clearColor];
    [self setRandomLocationForView:label];
    [self.kitchenSink addSubview:label];
}

- (void)askerViewController:(AskerViewController *)sender didAskQuestion:(NSString *)question andGotAnswer:(NSString *)answer
{
    [self addLabel:answer];
    [self dismissModalViewControllerAnimated:YES];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
    return YES;
}

@end

 

posted on 2013-03-23 23:57  写下一生的程序  阅读(2627)  评论(0编辑  收藏  举报