版本记录
版本号 | 时间 |
---|---|
V1.0 | 2018.08.04 |
前言
iOS中的视图加载可以有两种方式,一种是通过xib加载,另外一种就是通过纯代码加载。它们各有优点和好处,xib比较直观简单,代码比较灵活但是看着很多很乱,上一家公司主要风格就是用纯代码,这一家用的就是xib用的比较多。这几篇我们就详细的介绍一个xib相关知识。感兴趣的可以看上面写的几篇。
1. xib相关(一) —— 基本知识(一)
2. xib相关(二) —— 文件冲突问题(一)
3. xib相关(三) —— xib右侧标签介绍(一)
4. xib相关(四) —— 连线问题(一)
5. xib相关(五) —— 利用layout进行约束之界面(一)
6. xib相关(六) —— 利用layout进行约束之说明和注意事项(二)
7. xib相关(七) —— Storyboard中的segue (一)
8. xib相关(八) —— Size Classes(一)
9. xib相关(九) —— 几个IB修饰符(一)
10. xib相关(十) —— xib的国际化(一)
11. xib相关(十一) —— xib的高冷用法之修改视图的圆角半径、边框宽度和颜色(一)
12. xib相关(十二) —— UIStackView之基本介绍(一)
13. xib相关(十三) —— UIStackView之枚举UIStackViewDistribution使用(二)
14. xib相关(十四) —— UIStackView之UIStackViewAlignment使用(三)
15. xib相关(十五) —— UIStackView之工程实践(四)
16. xib相关(十六) —— UINib之基本介绍(一)
17. xib相关(十七) —— UINib之Introduction(二)
18. xib相关(十八) —— UINib之Nib文件(三)
19. xib相关(十九) —— UINib之Nib文件(四)
20. xib相关(二十) —— UINib之字符串资源(五)
21. xib相关(二十一) —— UINib之图像、声音和视频资源(六)
22. xib相关(二十二) —— UINib之数据资源文件(七)
直接拖动控件到xib
这个是最简单的用法,就是直接拖动控件到xib中,这个首先我们创建个自定义VC,并勾选上创建Also create XIB file
,如下所示:
在下面方法中进行实例化,并设置为window的根控制器。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
//这个就是从xib中加载VC对应的视图
JJMainVC *mainVC = [[JJMainVC alloc] initWithNibName:@"JJMainVC" bundle:nil];
self.window.rootViewController = mainVC;
[self.window makeKeyAndVisible];
return YES;
}
下面将JJMainVC的根view设置为蓝色,并run,可以看见蓝色背景图。
可以看见模拟器运行正常
接着我们就拖进去一个UIView系统的控件,如下所示:
运行起来,看一下模拟器
这个是入门级别的,其实可以略过去,但是之所以写出来,是我坚持的一贯的从简入繁的原理,这样有一个过程。
可以给UIImageView拖子控件吗
下面我们一起看一个问题,首先我们往xib中拖进去一个UIImageView并设置如下约束。
可以看见左边的层级关系,UIView和UIImageView都是一个层级上的,下面我们就给UIView上拖一个子控件UILabel,如下所示:
大家可以看见,这个拖进去的UILabel自然就成为了UIView的子控件,看左边的树形图可以清晰的看出来层级关系。
那么下面我们就进入正题了,我们可以给UIImageView控件像给UIView一样拖一个子控件吗?下面我们就试试。
可以看见,我们已经给UIImageView上面拖一个UILabel,但是系统不会让这个UILabel作为UIImageView的子视图,依然会将其和UIImageView作为平级进行展示,如下所示:
所以,通过这个简单的示例我们需要知道:
- 如果一个视图需要拖进去很多视图作为其子视图的情况,那么我们最好使用UIView作为其背景控件,不要使用UIImageView,因为一旦你使用了UIImageView,以后要是想往上面拖动控件就无法实现,到时候还是要修改底层的控件类型。
- 对于UILabel如果不是业务需求,最好不要设置宽高,只要确定x,y就可以了,具体大小可以靠内容自动填充。
- 同样,对于UIImageView,可以只指定x,y,具体的宽高可以根据图片本身的size去撑大,但是这么做的前提就是设计给的图不是很大,如果设计给的图不合规格,要么你让设计重新出规格的图,要不就添加宽高的约束并设置
contentMode
,防止失真。
所以这个话题的结论就是不可以,尽量用UIView做底部父控件,除非你确定这个地方就是一个UIImageView并且长时间不用改变,那你就用UIImageView。
VC中添加xib子view
这里其实是另外一个使用场景,在根View中拖进去一个UIView作为其子控件,这个子控件是一个自定义的UIView,是用xib进行实例化的。
也就是说上面的这个子视图还是一个xib关联的视图。
1. 第一种实现方式
首先我们需要自定义一个该子View的实例化xib对象。
我们首先写一个UIView的分类,这个分类用来实例化xib。
看一下子View中的xib内容,如下所示。
#import "UIView+JJRootView.h"
@implementation UIView (JJRootView)
+ (instancetype)fromNib {
UINib *nib = [UINib nibWithNibName:NSStringFromClass(self) bundle:nil];
NSArray * views = [nib instantiateWithOwner:nil options:nil];
for (UIView * view in views) {
if ([view isKindOfClass:[self class]]) {
return view;
}
}
return nil;
}
@end
然后在VC中自定义view属性,并实例化和添加约束。
#import "JJMainVC.h"
#import "JJView.h"
#import "UIView+JJRootView.h"
#import "Masonry.h"
@interface JJMainVC ()
@property (nonatomic, strong) JJView *jjView;
@end
@implementation JJMainVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
self.jjView = [JJView fromNib];
[self.view addSubview:self.jjView];
[self.jjView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).offset(100.0);
make.top.equalTo(self.view).offset(100.0);
make.width.equalTo(@200);
make.height.equalTo(@100);
}];
}
@end
通过上面的代码就可以将自定义的xib放在VC的根View中了,这里有几点需要注意:
- 在VC根view中我们什么都没做,加jjView子View是通过代码添加上去的。
- 在JJView的xib中,视图所属类关联到JJView,但是File's Owner里面什么都不要做。具体如下所示:
我们在模拟器中run一下,如下所示:
可见,根据xib实例化的JJView视图添加到VC的根view上了。
2. 第二种实现方式
1)在VC的根view上添加子视图并进行关联
在这种方式中,我们要在VC中的根view中首先拖进去一个view,并将其和JJView关联。
并设置好约束
在VC中进行拖线,如下所示:
这样拖进去就是实例化了,就不用像上面那样fromNib实例化和addSubview了。也就是说VC中什么代码都不用写了,如下所示:
#import "JJMainVC.h"
#import "JJView.h"
#import "UIView+JJRootView.h"
#import "Masonry.h"
@interface JJMainVC ()
@property (weak, nonatomic) IBOutlet JJView *jjView;
@end
@implementation JJMainVC
#pragma mark - Override Base Function
- (void)viewDidLoad
{
[super viewDidLoad];
}
@end
2)子JJView中进行拖线和关联设置
首先进行关联设置,如下所示:
可以看见,在File's Owner中进行关联设置,没有在View那一栏进行了关联。
下一步就是拖线了,将view拖线到JJView的m文件中。
然后在JJView中用下面代码进行加载。
#import "JJView.h"
@interface JJView()
@property (weak, nonatomic) IBOutlet UIView *contentView;
@end
@implementation JJView
#pragma mark - Override Base Function
- (void)awakeFromNib
{
[super awakeFromNib];
[[NSBundle mainBundle] loadNibNamed:@"JJView" owner:nil options:nil];
[self addSubview:self.contentView];
}
@end
运行起来可以看见崩溃了
2018-08-04 17:15:34.847414+0800 JJXib[957:25807] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x600000206a40> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key contentView.'
*** First throw call stack:
(
0 CoreFoundation 0x00000001065551e6 __exceptionPreprocess + 294
1 libobjc.A.dylib 0x0000000105bea031 objc_exception_throw + 48
2 CoreFoundation 0x00000001065550b9 -[NSException raise] + 9
3 Foundation 0x000000010560bb47 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 292
4 UIKit 0x0000000106e5be8a -[UIRuntimeOutletConnection connect] + 109
5 CoreFoundation 0x00000001064f7e8d -[NSArray makeObjectsPerformSelector:] + 317
6 UIKit 0x0000000106e5a834 -[UINib instantiateWithOwner:options:] + 1856
7 UIKit 0x0000000106e622ed -[NSBundle(UINSBundleAdditions) loadNibNamed:owner:options:] + 222
8 JJXib 0x00000001052bf1ac -[JJView awakeFromNib] + 140
9 UIKit 0x0000000106e5aa61 -[UINib instantiateWithOwner:options:] + 2413
10 UIKit 0x0000000106b750d7 -[UIViewController _loadViewFromNibNamed:bundle:] + 383
11 UIKit 0x0000000106b75a04 -[UIViewController loadView] + 177
12 UIKit 0x0000000106b75d21 -[UIViewController loadViewIfRequired] + 175
13 UIKit 0x0000000106b76574 -[UIViewController view] + 27
14 UIKit 0x0000000106a44123 -[UIWindow addRootViewControllerViewIfPossible] + 122
15 UIKit 0x0000000106a44834 -[UIWindow _setHidden:forced:] + 294
16 UIKit 0x0000000106a575cc -[UIWindow makeKeyAndVisible] + 42
17 JJXib 0x00000001052ca084 -[AppDelegate application:didFinishLaunchingWithOptions:] + 516
18 UIKit 0x00000001069c96fb -[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 278
19 UIKit 0x00000001069cb172 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 4123
20 UIKit 0x00000001069d05cb -[UIApplication _runWithMainScene:transitionContext:completion:] + 1677
21 UIKit 0x0000000106d92f7e __111-[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:]_block_invoke + 866
22 UIKit 0x0000000107165a39 +[_UICanvas _enqueuePostSettingUpdateTransactionBlock:] + 153
23 UIKit 0x0000000106d92bba -[__UICanvasLifecycleMonitor_Compatability _scheduleFirstCommitForScene:transition:firstActivation:completion:] + 236
24 UIKit 0x0000000106d933db -[__UICanvasLifecycleMonitor_Compatability activateEventsOnly:withContext:completion:] + 675
25 UIKit 0x0000000107704614 __82-[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:]_block_invoke + 299
26 UIKit 0x00000001077044ae -[_UIApplicationCanvas _transitionLifecycleStateWithTransitionContext:completion:] + 433
27 UIKit 0x00000001073e875d __125-[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:]_block_invoke + 221
28 UIKit 0x00000001075e34b7 _performActionsWithDelayForTransitionContext + 100
29 UIKit 0x00000001073e8627 -[_UICanvasLifecycleSettingsDiffAction performActionsForCanvas:withUpdatedScene:settingsDiff:fromSettings:transitionContext:] + 223
30 UIKit 0x00000001071650e0 -[_UICanvas scene:didUpdateWithDiff:transitionContext:completion:] + 392
31 UIKit 0x00000001069ceeac -[UIApplication workspace:didCreateScene:withTransitionContext:completion:] + 515
32 UIKit 0x0000000106fa1bcb -[UIApplicationSceneClientAgent scene:didInitializeWithEvent:completion:] + 361
33 FrontBoardServices 0x000000010adff2f3 -[FBSSceneImpl _didCreateWithTransitionContext:completion:] + 331
34 FrontBoardServices 0x000000010ae07cfa __56-[FBSWorkspace client:handleCreateScene:withCompletion:]_block_invoke_2 + 225
35 libdispatch.dylib 0x0000000109f357ec _dispatch_client_callout + 8
36 libdispatch.dylib 0x0000000109f3adb8 _dispatch_block_invoke_direct + 592
37 FrontBoardServices 0x000000010ae33470 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 24
38 FrontBoardServices 0x000000010ae3312e -[FBSSerialQueue _performNext] + 439
39 FrontBoardServices 0x000000010ae3368e -[FBSSerialQueue _performNextFromRunLoopSource] + 45
40 CoreFoundation 0x00000001064f7bb1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
41 CoreFoundation 0x00000001064dc4af __CFRunLoopDoSources0 + 271
42 CoreFoundation 0x00000001064dba6f __CFRunLoopRun + 1263
43 CoreFoundation 0x00000001064db30b CFRunLoopRunSpecific + 635
44 GraphicsServices 0x000000010b6c8a73 GSEventRunModal + 62
45 UIKit 0x00000001069d2057 UIApplicationMain + 159
46 JJXib 0x00000001052bed0f main + 111
47 libdyld.dylib 0x0000000109fb2955 start + 1
48 ??? 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException
问题就出在下面这句
[[NSBundle mainBundle] loadNibNamed:@"JJView" owner:nil options:nil];
这里设置了File's Owner,那么这句话owner这个参数就不能传递为nil,应该为self,修改下如下所示:
[[NSBundle mainBundle] loadNibNamed:@"JJView" owner:self options:nil];
运行起来查看结果,如下所示:
可见子视图已经加载上来了,且如约束所示,加在了视图的中间。
这里你可以不在下面这里这么写:
- (void)awakeFromNib
{
[super awakeFromNib];
[[NSBundle mainBundle] loadNibNamed:@"JJView" owner:nil options:nil];
[self addSubview:self.contentView];
}
你也可以用下面这几行代码进行替换
@property (nonatomic, weak) IBOutlet UIView *view;
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
self.backgroundColor = [UIColor clearColor];
NSString *className = NSStringFromClass([self class]);
self.view = [[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil] firstObject];
self.view.frame = self.bounds;
[self addSubview:self.view];
return self;
}
return nil;
}
- (void)awakeFromNib
{
[super awakeFromNib];
}
运行起来效果是一样的。
后记
本篇主要讲述了xib几种常用的用法,感兴趣的给个赞或者关注~~~~