Nested Xib Views - 使用XIB实现嵌套自定义视图

转自:http://www.tuicool.com/articles/ENv6Nf

在进行 iOS 开发的过程中,对于一些复杂的界面,我们可以通过 Interface
Builder
 这个 Xcode 集成的可视化界面编辑工具在完成,这回节省大部分时间以及代码量。它的使用方法这里不做介绍了,这次我要介绍是使用它来实现一个嵌套的自定义视图。解释一下就是,我们使用 IB 自定义了一个 View ,然后又在其他的 xib 文件中使用了这个 View ,那么这就是所谓的嵌套自定义视图。之所以要介绍它,是因为我自己在使用它的时候遇到了一些问题,一方面写下来做个记录供自己查看,另一方面我相信大家在使用的时候应该也会遇到这样的问题,方便大家。

下面使用的示例代码我已经放到 Github 上了, 项目地址 ,有需要的朋友可以去查看, Demo 非常简单,主要是介绍这个知识点。

Question

首先我们创建一个 SingleView 的工程,项目使用 StoryBoard ,(使用 Xib 也无所谓,因为有些老的项目可能还没有使用到 StoryBoard ),然后创建一个 CustomView 作为我们的自定义视图。

有时对于复杂的界面我们可能会拆分出来对它进行单独处理,又有可能它的界面布局很复杂,这时我们就会用 Interface Builder 对它的布局进行处理。这里的 CustomView 就是这样一个视图,所以我们为它创建一个 xib 文件,我们通常的作法就是把 xib 中的 View 的 custom
class
 更改为我们的 CustomView 。

接下来对我们的界面进行布局,并连接输出口,编写响应逻辑,这里我放了一个 ImageView 和一个 Label 在这里,并把 View 的背景色设置为浅灰色。

自定义的 View 制作完成,回到我们 ViewController 的 xib 文件,拖入两个 View 并把他们的 custom
class
 更改为 CustomView 。

这时,我们算是工作做完了,运行程序,结果悲剧了,怎么不是我们想要的结果,为什么只生成了两个空白的视图,我们视图上的图片和文字哪里去了?

在 CustomView 中的 awakeFromeNib 方法中增加断点调试发现,在 CustomView 初始化完成后, ImageView 和 Label 并没有被初始化,他们仍然是 nil 。这就是在嵌套使用 xib 自定义视图时非常容易出现的问题,我们觉得被嵌套的视图能够正常显示出来,但是实际上它并没有被按照我们在 xib 上指定的方式被初始化。

Solution

那么如何解决这种问题,以及这种问题又是如何出现的呢?其实这主要是由于我们对 xib 文件的加载原理不熟悉所导致的,我们以为定义一个 View ,创建一个 xib 文件并布局好它的子视图,让后将它使用在另外一个 xib 文件中,把 custom
class
 改成它,然后 xib 的加载系统会自动为我们做好其余的一切。其实并不是这样的。

这样做 xib 加载系统只会为我们创建一个 CustomView 的对象,但这并不包括 CustomView 所对应的 xib 文件中的部分,所以只创建了一个空白的 View 。

解决他们有两种方式,不过最终的思路都是通过代码强制使 CustomView 的 xib 部分被加载。第一种是通过代码创建 CustomView 的对象,然后 addSubview 到 viewController 的 view 上。第二种是在 CustomView 的实现文件里,通过重载一些方法,来完成加载 xib 文件。

这两种方法各有利弊,第一种使用起来方便也好理解,但是当嵌套的层级比较多的时候或者一个 View 中有多个这样的 CustomView 时,这种方式就会显得过于麻烦。而第二种虽然理解起来有些难度,但是当你处理好之后,直接在需要的 xib 文件中拖入 view ,改个 custom
class
 ,就能直接生成需要的对象了,并且也能够在 xib 中对他们进行直接布局,不再需要用代码去布局了。

NO 1.

先来介绍第一种方法,很简单,就是找到 xib 文件,生成对象,设置属性, addsubview 到视图上。

NO 2.

第二种方法是通过重载 initWithCoder 方法来实现,因为通过 xib 来创建一个对象会调用到这个方法,所以我们需要在这个方法里做一些处理,把这个 CustomView 的 xib 中的内容加载进来,这时同样是需要通过代码来来加载,首先附上代码

 
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super initWithCoder:aDecoder]) {
        UIView *containerView = [[[UINib nibWithNibName:@"CustomView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
        CGRect newFrame = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
        containerView.frame = newFrame;
        [self addSubview:containerView];
    }
    return self;
}

此外,还要这里的输出口以及设置 custom class 的位置跟第一种方式有所不同,这里需要取消掉 xib 中 view 的 custom
class
 ,再将跟它连接的图片与文字的输出口取消掉,在这里这个 view 只是被当做一个容器来处理,它跟Customview 没有直接关系,它将来会被 addSubview 到 CustomView 上,除此之外还要把 xib 的 File‘s
ower
 的 custom class 改成 CustomView ,表示这个 xib 文件的持有者是 CustomView 。再把它与图片和文字通过输出口连接起来。

这个时候在运行程序就看到了我们想要的结果了。 _^

其实想要实现第二种解决方案所要的效果,还有一种方式,它是通过重载 awakeAfterUsingCoder: 方法来实现的,这个方法的返回值会替换掉真正的加载对象,所以在具体的加载 CustomView 的方式又与第一种相同,所以 xib 的输出口连接与 custom
class
 的设置也与第一种解决方案相同。不过这种方式是更复杂也更难于理解的,不推荐使用,因为上一个方法就能很好的解决这个问题了,这里只是贴出这个方法的代码,有想仔细研究的请参看文章底部的参考文章。

 
- (id) awakeAfterUsingCoder:(NSCoder*)aDecoder {
    BOOL isJustAPlaceholder = ([[self subviews] count] == 0);
    if (isJustAPlaceholder) {
        CustomView* theRealThing = [[self class] getClassObjectFromNib];

        theRealThing.frame = self.frame;

        // make compatible with Auto Layout
        self.translatesAutoresizingMaskIntoConstraints = NO;
        theRealThing.translatesAutoresizingMaskIntoConstraints = NO;

        // convince ARC that we‘re legit, unnecessary since at least Xcode 4.5
        CFRelease((__bridge const void*)self);
        CFRetain((__bridge const void*)theRealThing);

        return theRealThing;
    }
    return self;
}

时间: 2024-08-26 14:33:56

Nested Xib Views - 使用XIB实现嵌套自定义视图的相关文章

iOS-xib(使用XIB实现嵌套自定义视图)

参考:http://wtlucky.github.io/geekerprobe/blog/2014/08/10/nested-xib-views/?utm_source=tuicool 因为主要练习xib就使用xib上的方式 1.首先创建一个自定义视图类 2.清除view的链接(没有进行这一步的略过),保证为上面的格式 3.建立新的链接 在这里添加新的链接 这是拖拽过去的,你懂的- 4.在CustomView.m重新view的- (id)initWithCoder:(NSCoder *)aDec

Custom UIView 通过xib实现一个简单地自定义视图

1 /** 2 * 使用Custom UIView的这条路,我走的很不顺利,前期犯2,中期有事,浪费了很多时间! 3 * 现在空闲的时间不多了,只能抓紧时间写两个小demo,一来做练习,二来整理下也许能给要学习的人一些帮助! 4 */ 1 /** 2 * Custom UIView常用的三种实现方法: 3 * Method_1:通过初始化的方式(eg:UITableViewCell的重写) 4 Method_2:通过drawRect的方法,使用代码自己画出想要的视图 5 Method_3:通过x

XIB自定义视图的整理

- (void)setAppInfo:(AppInfo *)appInfo { _appInfo = appInfo; _icon.image = appInfo.image; _label.text = appInfo.name; } + (instancetype)appView { // appView要加载NIB文件,使用[[NSBundle mainBundle] loadNibNamed: owner: options:] return [[[NSBundle mainBundle]

**IOS:xib文件解析(xib和storyboard的比较,一个轻量级一个重量级)

使用Xcode做iOS项目,经常会和Xib文件打交道,因为Xib文件直观的展现出运行时视图的外观,所以上手非常容易,使用也很方便,但对于从未用纯代码写过视图的童鞋,多数对Xib的理解有些片面. Xib文件是什么? A nib file describes the visual elements of your application’s user interface, including windows, views, controls, and many others. It can also

自定义视图

1.创建一个视图类实现spring的View接口,并且把视图类用@Component注解为sprinmvc组建(重要) package com.hy.springmvc.views; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Compo

iOS7新特性 ViewController转场切换(三) 自定义视图控制器容器的切换---非交互式

@继续前面的内容,这一章,主要介绍自定义ViewController容器上视图VC的切换.先来看看系统给我们提供的容器控制器 UINavigationController和UITabBarController 都有一个NSArray类型的属性viewControllers,很明显,存储的就是需要切换的视图VC.同理,我们定义一个ContainerViewController,是UIViewController的直接子类,用来作为容器依托,额,其他属性定义详见代码吧,这里不多说了.(PS:原先我进

Android自定义视图四:定制onMeasure强制显示为方形

这个系列是老外写的,干货!翻译出来一起学习.如有不妥,不吝赐教! Android自定义视图一:扩展现有的视图,添加新的XML属性 Android自定义视图二:如何绘制内容 Android自定义视图三:给自定义视图添加"流畅"的动画 Android自定义视图四:定制onMeasure强制显示为方形 上一篇开发之后的效果如上图.不过看着这张图,需要注意的不是我们自定义视图展示了什么,而是这个视图的大小和位置.你会看到这个折线图有一个特定的大小(size).这个size是怎么定的呢?现在的代

ANDROID自定义视图——onMeasure流程,MeasureSpec详解

简介: 在自定义view的时候,其实很简单,只需要知道3步骤: 1.测量--onMeasure():决定View的大小 2.布局--onLayout():决定View在ViewGroup中的位置 3.绘制--onDraw():如何绘制这个View. 而第3步的onDraw系统已经封装的很好了,基本不用我们来操心,只需要专注到1,2两个步骤就中好了. 而这篇文章就来谈谈第一步,也是十分关键得一步:"测量(Measure)" Measure(): Measure的中文意思就是测量.所以它的

andriod自定义视图

一.通过View实现自定义视图 通过构造函数创建可视界面 public class MyView extends View { // Constructor required for in-code creation   public MyView(Context context) {     super(context);   } // Constructor required for inflation from resource file   public MyView (Context