美文网首页iOS
xib相关(二十三) —— 几个xib使用场景示例(一)

xib相关(二十三) —— 几个xib使用场景示例(一)

作者: 刀客传奇 | 来源:发表于2018-08-04 17:52 被阅读0次

    版本记录

    版本号 时间
    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几种常用的用法,感兴趣的给个赞或者关注~~~~

    相关文章

      网友评论

        本文标题:xib相关(二十三) —— 几个xib使用场景示例(一)

        本文链接:https://www.haomeiwen.com/subject/inmpvftx.html