摘要
本文通过一个实例:模拟建立联系人管理app,提供基本的添加和删除联系人功能,来学习xib的基本操作。
xib基本知识总结
- 可以认为xib和nib是同义词
- 加载xib文件的时候,会创建objects下面的所有控件,并且按顺序装到数组中返回。
storyboard和xib的区别:
1>区别:
storyboard:描述软件界面,大范围,重量级,比较适合描述整个软件的所有界面
xib:描述软件的界面,小范围,轻量级,适合描述某个小界面,局部界面。
2> 相同点:本质都是代码file’owner的使用步骤
在xib文件中设置file’s owner的类属性(目的是在xib中能够找到owner的方法和属性)
建立file’s owner跟控件的联系
利用代码加载xib,传递owner参数(类型一定要匹配)。
实例
viewController h代码
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (weak, nonatomic) IBOutlet UIBarButtonItem *delBut;
- (IBAction)addContact:(UIBarButtonItem *)sender;
- (IBAction)delContact:(UIBarButtonItem *)sender;
@end
viewController m代码
//
// ViewController.m
// 联系人管理—简单的添加删除
//
// Created by dqw on 15/5/9.
// Copyright (c) 2015年 itcast. All rights reserved.
//
#import "ViewController.h"
#import "NewContact.h"
#define kDuration 1.0
#define kHeight 30
#define kTag 10
#define kWidth 320
@interface ViewController ()
{
NSArray *_contNames;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 定义联系人名字数组。
_contNames = @[@"贾某", @"张某", @"丁某", @"李某"];
}
#pragma mark 1. 定义添加联系人的方法
#pragma mark 重点:重点:如此多的代码都可以被xib来代替。
- (UIView *)addContactM
{
#pragma mark 创建联系人view。
#pragma mark 注意点1:subviews中的元素,要定义一个uiview的指针才能取出其中的属性。
// subviews数组的元素默认是id类型的,所以xxsubviews.lastobject.framexx是不被认可的表达。
// 定义联系人view的位置。
UIView *lastView = self.view.subviews.lastObject;
CGFloat newContPosy = lastView.frame.origin.y + lastView.frame.size.height + 1;
// 此处是要创建一个新对象的,要为其分配空间,不能像上面那样仅仅是一个指针。
UIView *newContact = [[UIView alloc] initWithFrame:CGRectMake(320, newContPosy, kWidth, kHeight)];
// 设置联系人view。
newContact.backgroundColor = [UIColor redColor];
newContact.alpha = 0;
[self.view addSubview:newContact];
#pragma mark 创建联系人头像
// 定义头像图片。
int no = arc4random_uniform(9);
NSString *imgName = [NSString stringWithFormat:@"01%d.png", no];
UIImage *contIconImg = [UIImage imageNamed: imgName];
UIButton *contIcon = [[UIButton alloc]initWithFrame:CGRectMake(5, 0, 30, 30)];
[contIcon setBackgroundImage:contIconImg forState:UIControlStateNormal];
[contIcon addTarget:self action:@selector(printContName:) forControlEvents:UIControlEventTouchUpInside];
[newContact addSubview:contIcon];
#pragma mark 创建联系人名字
UILabel *contName = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, kWidth, kHeight)];
no = arc4random_uniform(_contNames.count);
contName.text = _contNames[no];
contName.textAlignment = 1;
contName.tag = kTag;
[newContact addSubview:contName];
#pragma mark 创建精确删除联系人按钮
UIButton *delCurBut = [UIButton buttonWithType:UIButtonTypeRoundedRect];
delCurBut.frame = CGRectMake(280, 0, 30, 30);
[delCurBut setTitle:@"删除" forState:UIControlStateNormal];
[delCurBut addTarget:self action:@selector(delCurCont:) forControlEvents:UIControlEventTouchUpInside];
[newContact addSubview:delCurBut];
return newContact;
}
#pragma mark 2. 定义打印联系人名字的方法(供头像点击事件调用)
- (void)printContName:(UIButton *)sender
{
#pragma mark 重点1:由view得到兄弟view,不用再@Property成员。
UILabel *contName = [[sender superview] viewWithTag:kTag];
//注意类型转换,多态。
// NSLog(@"%@", ((UILabel *)[[sender superview] viewWithTag:kTag]).text);
NSLog(@"%@", contName.text);
}
#pragma mark 3. 监听添加联系人。(单击了添加按钮,按钮就是该方法的调用者)。
- (IBAction)addContact:(UIBarButtonItem *)sender {
#pragma mark 注意点2:设置enabled属性的位置。
_delBut.enabled = YES;
// UIView *aContact = [self addContactM];
#pragma mark 重点:重点:体会将xib封装在一个类中的好处。
NewContact *aContact = [NewContact contactIcon:[NSString stringWithFormat:@"01%d.png", arc4random_uniform(9)] Name:_contNames[arc4random_uniform(4)]];
UIView *lastView = self.view.subviews.lastObject;
CGFloat newContPosy = lastView.frame.origin.y + lastView.frame.size.height + 1;
aContact.frame = CGRectMake(kWidth, newContPosy, kWidth, kHeight);
[aContact.delBut addTarget:self action:@selector(delCurCont:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:aContact];
// 定义动画。
[UIView animateWithDuration:kDuration animations:^{
CGRect tempFrame = aContact.frame;
tempFrame.origin.x = 0;
aContact.frame = tempFrame;
aContact.alpha = 1;
}completion:^(BOOL finished){
}];
}
#pragma mark 4. 监听删除联系人。
- (IBAction)delContact:(UIBarButtonItem *)sender {
#pragma mark 注意点3:定义在代码块外部,供多个代码块使用。
UIView *lastView = self.view.subviews.lastObject;
[UIView animateWithDuration:kDuration animations:^{
// 如果放在这里,其不能被completion里面的代码调用。
// UIView *lastView = self.view.subviews.lastObject;
CGRect tempFrame = lastView.frame;
tempFrame.origin.x = 320;
lastView.frame = tempFrame;
lastView.alpha = 0;
}completion:^(BOOL finished){
NSLog(@"1——%d", self.view.subviews.count);
[lastView removeFromSuperview];
NSLog(@"2——%d", self.view.subviews.count);
// 放在这里是正确的。
_delBut.enabled = (self.view.subviews.count > 1);
}];
#pragma mark 重点2:体会动画方法中completion里面代码的执行顺序。
// 该打印是在删除之前,所以下面的判断也是在删除之前,所以判断无效。
NSLog(@"3——%d", self.view.subviews.count);
// _delBut.enabled = (self.view.subviews.count > 1);
}
#pragma mark 5. 删除当前联系人。
- (void)delCurCont:(UIButton *)sender
{
#pragma mark 注意点4:进一步体会定义指代指针的位置,与subviews和count对比。
//因为其在后面使用中没有被改变,索引可用在这里定义,用superView来指代它,但是类似于subviews和count等等,因为下面会对其更改,所以还是使用其本身,以保证值的更新。
UIView *superView = sender.superview;
[UIView animateWithDuration:kDuration animations:^{
CGRect tempF = superView.frame;
tempF.origin.x = kWidth;
superView.frame = tempF;
superView.alpha = 0;
}completion:^(BOOL finished) {
#pragma mark 注意点5:索引的当前superview的位置,体会代码的位置。
int curIndex = [self.view.subviews indexOfObject:superView];
[superView removeFromSuperview];
[UIView animateWithDuration:0.5 animations:^{
// 将下面的联系人依次上移。
for (int i = curIndex; i < self.view.subviews.count; i++) {
UIView *tempView = self.view.subviews[i];
CGRect tempF = tempView.frame;
tempF.origin.y -= kHeight + 1;
tempView.frame = tempF;
}
}];
// 继续判断删除的按钮的是否可用。
_delBut.enabled = self.view.subviews.count > 1;
}];
}
#pragma mark 重中之重+不解之处:file‘s owner的设置:设置file'owner的话,虽然可以连线,但是运行出错,而view的类属性设为newcontact的话 ,则可以运行。????
@end
newcontact h代码
#import <UIKit/UIKit.h>
@interface NewContact : UIView
#pragma mark 重点1:只有将xib文件中任意一个view的类属性是NewContact,则其中的所有view的类属性默认都是NewContact,就所有的view里面的控件都可以和.h文件里面的属性和方法建立联系。
@property (weak, nonatomic) IBOutlet UIButton *icon;
@property (weak, nonatomic) IBOutlet UILabel *name;
@property (weak, nonatomic) IBOutlet UIButton *delBut;
+ (NewContact *)contactIcon:(NSString *)iconNaem Name:(NSString *)name;
@end
newcontact m代码
#import "NewContact.h"
@implementation NewContact
#pragma mark 1. 将新联系人的创建和部分初始化包装成newcontact的一个类方法。
+ (NewContact *)contactIcon:(NSString *)iconNaem Name:(NSString *)name
{
#pragma mark 重点1:owner的使用,如果xib里面的东西要用到对象方法,则必须要制定确切的owner(是个对象),此处是个类方法,所以可以不用指定owner。
NewContact *aContact = [[NSBundle mainBundle] loadNibNamed:@"NewContact" owner:nil options:nil][0];
// 在方便的情况下,能够放在这里初始化的就放在这里初始化。
aContact.backgroundColor = [UIColor redColor];
aContact.alpha = 0;
return aContact;
}
@end
重点 难点 注意点
- subviews中的元素默认是id类型的,要定义一个uiview类型的指针指向它,才能间接得到其中元素的属性。
- 体会仅仅创建指针指向某一个已存在的对象,和创建一个新对象的使用场合。
- 如何有当前view得到其兄弟view,通过父view和tag间接得到,注意类型转换。
- 动画方法中completion中的代码,是要在动画执行完毕后采执行,是动画执行的过程中,执行动画后面的代码,动画执行完毕,再返回来执行completion中的代码。
- 重中之重+不解之处:file‘s owner的设置:设置file’owner的话,虽然可以连线,但是运行出错,而view的类属性设为newcontact的话 ,则可以运行,此时利用loadnibxx方法返回的对象就是newcontact类型的。
- owner的使用,如果xib里面的东西要用到对象方法,则必须要制定确切的owner(是个对象),此处是个类方法,所以可以不用指定owner。
- 只有将xib文件中任意一个view的类属性是NewContact,则其中的所有view的类属性默认都是NewContact,所有的view里面的控件都可以和.h文件里面的属性和方法建立联系。
- 创建随机数的方法
- 类延展的使用,定义供多个方法使用的变量。
- xib的sh属性要设置为freeXX类型,才能调整高度。
- 本例中需要注意按钮的enabled属性的设置位置(时间),得到subviews中元素或者个数的时间。
- 属性后面有animationable修饰的话,说明它是可以添加动画的。
疑问
- file’s owner中的owner和loadnibxx里面的owner是不是一回事??
- file’owner 的类属性设置为newcontact类后,可以和newcontact。h中的属性和方法连线,但是运行错误。而如果将xib文件的view类属性进行同样的设置的话,则运行正常。
- loadnibnamed 里面的owner 究竟指谁?
- addtarget里面的target指的又是谁?
- viewController里面的self指的又是谁?
- uibutton type roundedrect 是不是就是带边框的。我设置的为什么没有边框。老师的例子,放在我这运行,视频的上的边框就没了,估计是软件版本的问题。
- 修改名字的两个方法:1. 3出修改,记得setting里面的修改)2. setting里面的修改如果不管用的话,则用删除再添加的方法。试了,没有成功。
- bar button item 添加图片,有背景色的图片,添加后都是蓝色的小实心圆,一个红色的x,没有背景色,添加后变成蓝色了。看了它的属性,有image 属性,但是好像没有color相关的。
待完善的内容
- 提高调试的能力,仔细分析所出现的错误。如本例中出现的错误有不稳定的特性,就要去怀疑是不是产生随机数处的问题。
- 更改名字的方法。