iOS开发基础24-UIPickerView、UITextField、KVC、UIDatePicker、控制器及导航控制器

在iOS开发中,涉及的UI组件和类非常丰富。本文将详细介绍UIPickerView的使用、注册界面中UITextField的处理、自定义键盘的实现、控制器的创建和管理、以及导航控制器的使用。

一、UIPickerView的简单使用

1. UIPickerViewDataSource

要使用UIPickerView,首先需要实现其数据源协议UIPickerViewDataSource。以下两个方法必须实现:

// 返回有多少列
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
    return 1; // 示例中返回1列
}

// 返回第component列有多少行
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
    return 10; // 示例中返回10行
}

2. UIPickerViewDelegate

UIPickerViewDelegate包括了定义UIPickerView外观和行为的方法。以下是一些常用的方法:

// 返回第component列的宽度
- (CGFloat)pickerView:(UIPickerView *)pickerView widthForComponent:(NSInteger)component {
    return 100.0; // 示例中宽度为100.0
}

// 返回第component列的行高
- (CGFloat)pickerView:(UIPickerView *)pickerView rowHeightForComponent:(NSInteger)component {
    return 40.0; // 示例中行高为40.0
}

// 返回第component列第row行的标题
- (NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component {
    return [NSString stringWithFormat:@"Row %ld", (long)row];
}

// 返回第component列第row行的富文本标题
- (NSAttributedString *)pickerView:(UIPickerView *)pickerView attributedTitleForRow:(NSInteger)row forComponent:(NSInteger)component {
    NSString *title = [NSString stringWithFormat:@"Row %ld", (long)row];
    NSAttributedString *attributedTitle = [[NSAttributedString alloc] initWithString:title attributes:@{NSForegroundColorAttributeName:[UIColor redColor]}];
    return attributedTitle;
}

// 返回第component列第row行的视图控件
- (UIView *)pickerView:(UIPickerView *)pickerView viewForRow:(NSInteger)row forComponent:(NSInteger)component reusingView:(UIView *)view {
    UILabel *label = (UILabel *)view;
    if (!label) {
        label = [[UILabel alloc] init];
    }
    label.text = [NSString stringWithFormat:@"Row %ld", (long)row];
    label.textAlignment = NSTextAlignmentCenter;
    return label;
}

// 当用户选中某一行时调用
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
    NSLog(@"Selected Row: %ld", (long)row);
}

二、注册界面中的UITextField处理

1. UITextFieldDelegate

在注册界面中,有时需要拦截用户输入,通过实现以下UITextFieldDelegate方法来控制文本框行为。

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string {
    return NO; // 禁止用户手动输入,文本框内容只能选择
}

另外两个常用方法:

// 是否允许开始编辑
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
    return YES;
}

// 是否允许结束编辑
- (BOOL)textFieldShouldEndEditing:(UITextField *)textField {
    return YES;
}

2. 自定义键盘

通过设置UITextFieldinputView属性可以实现自定义键盘。例如,可自定义一个UIDatePicker作为生日选择器。

- (void)setUp {
    UIDatePicker *datePicker = [[UIDatePicker alloc] init];
    datePicker.datePickerMode = UIDatePickerModeDate;
    datePicker.locale = [NSLocale localeWithLocaleIdentifier:@"zh"];
    [datePicker addTarget:self action:@selector(dateChange:) forControlEvents:UIControlEventValueChanged];
    self.inputView = datePicker;
}

- (void)dateChange:(UIDatePicker *)datePicker {
    NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
    fmt.dateFormat = @"yyyy-MM-dd";
    self.text = [fmt stringFromDate:datePicker.date];
}

3. KVC的底层实现

键值编码(KVC)可以直接操作对象的属性,简化代码。在字典转模型时常用。

[model setValuesForKeysWithDictionary:dict];

其底层步骤:

  1. 遍历字典中的所有键值对。
  2. 使用setValue:forKey:为模型对象的属性赋值。

底层实现如下:

// 模型对象的属性赋值实现逻辑
- (void)setValue:(id)value forKey:(NSString *)key {
    if ([self respondsToSelector:NSSelectorFromString(setterMethodName)]) {
        // 1. 查找模型中是否有`set<Key>:`方法,如果存在则调用
        [self performSelector:NSSelectorFromString(setterMethodName) withObject:value];
    } else if ([self respondsToSelector:NSSelectorFromString(key)]) {
        // 2. 否则查找模型中是否有键名相同的属性,存在则直接赋值
        [self setValue:value forKeyProperty:key];
    } else if ([self respondsToSelector:NSSelectorFromString(underscoreKey)]) {
        // 3. 否则查找模型中是否有`_`开头的属性,存在则直接赋值
        [self setValue:value forKeyUnderscoreProperty:underscoreKey];
    } else {
        // 4. 都不存在则抛出异常
        @throw [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"Unknown key" userInfo:nil];
    }
}

三、通过Storyboard加载控制器

使用Storyboard可以简化UI设计和控制器管理。

// 加载Storyboard文件
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];

// 创建Storyboard描述的控制器
UIViewController *vc = [storyboard instantiateInitialViewController];

通过标识符创建控制器:

UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"yourIdentifier"];

四、通过XIB创建控制器

通过XIB文件,可以自定义控制器的视图。具体步骤如下:

  1. 设置XIB文件的File's Owner为对应的控制器。
  2. 通过连线指定控制器的View。

在代码中使用如下:

// 在AppDelegate中创建控制器
XMGViewController *vc = [[XMGViewController alloc] initWithNibName:nil bundle:nil];
self.window.rootViewController = vc;
[self.window makeKeyAndVisible];

底层逻辑:

  1. 优先查找指定nibName的XIB文件。
  2. 如果nibName为空,则根据类名寻找对应的XIB文件。
  3. 如果都未找到,则生成一个空的UIView

五、控制器的view创建

1. loadView方法

loadView方法在第一次访问控制器的view时调用,用来加载控制器的view,可以通过重写这个方法自定义view。

- (void)loadView {
    // 必须手动创建控制器的view
    self.view = [[UIView alloc] init];
    self.view.backgroundColor = [UIColor whiteColor];
}

2. xib加载控制器的view

通过init方法会调用initWithNibName:bundle:方法。如果nibName为空,会先查找同名的XIB文件。

3. 控制器view延迟加载

控制器的view是延迟加载的,也就是懒加载。只有当第一次访问view属性时,才会调用loadView方法。

- (UIView *)view {
    if (!_view) {
        [self loadView];
        [self viewDidLoad];
    }
    return _view;
}

4. 控制器view默认是几乎透明的

创建的控制器view默认是透明的,只有部分透明度,并且是最下层的view。

六、导航控制器的基本使用

导航控制器(UINavigationController)用于在多个视图控制器之间进行层级导航。

1. 设置根控制器

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    
    // 创建导航控制器的根控制器
    UIViewController *vc = [[OneViewController alloc] init];
    UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
    
    self.window.rootViewController = nav;
    [self.window makeKeyAndVisible];
    return YES;
}

2. 导航控制器的子控制器

导航控制器管理的子控制器可以直接通过navigationController属性访问其导航控制器,并进行页面跳转。

// 跳转到第二个界面
- (IBAction)jump2Two:(id)sender {
    TwoViewController *two = [[TwoViewController alloc] init];
    [self.navigationController pushViewController:two animated:YES];
}

3. initWithRootViewController底层实现

initWithRootViewController方法实际上调用了导航控制器的push方法,将根控制器作为其第一个控制器。

UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
// 调用push方法
// [nav pushViewController:vc animated:YES];
posted @ 2015-07-28 22:25  Mr.陳  阅读(1827)  评论(0编辑  收藏  举报