Interface Builder

作者: wpf_register | 来源:发表于2017-10-03 02:05 被阅读495次

    参考文档1
    参考文档2

    近来系统学习了《利用IB优雅地开发UI》,收获颇多。开始接触可视化编程时,非常喜欢,简单高效,可以少写很多代码,但后来实践中更多的使用的是纯代码。主要还是对XIB及SB好些特性不甚了解,自以为代码处理问题更加灵活,也更有优越感,实则不然。

    IB 从Xcode4开始集成到Xcode当中,成为IDE的一部分,之后每个版本都会对其进行改进加强,更多地体现出苹果对可视化编程的重视。

    IB 基础

    • Xib 是 .xib 扩展名的文本文件,以 XML 形式存储 UI 相关数据。xib可以不依赖源文件,无须与源文件“关联”,单独存在。
      SB 是 storyboard 扩展名的文本文件,以 XML 形式存储 UI 相关数据。
      xib 和 SB 经过编译后生成nib 文件和storyboardc文件
      SB可以理解成一组与ViewController关联的xib集合,该集合中同样保留着各VC间的跳转数据

    • xib与源文件关联,文件名称可以不相同,因为关联的其实是类,而非文件本身。
      创建VC时可以同步创建xib,但若不同步创建时需要特别注意:
      A方法:先创建空模板,拖入View,将其 File Owner 的Identity 设为对应的类,

      注意:还要将该VC的view与控件显示区的view边线关联。
      B方法:直接在空模板中创建VC,将其Identity设为对应的类,同时用nib加载(本质也说明一个xib可以创建多个UIView子类,或一个VC)。

     UINib  *nib = [UINib nibWithNibName:@"VCname" bundle:nil] ;
     VCname *vc = [nib instantiateWithOwner:nil options:nil ][0];
    
    • storyBoard 通过下面实例方法加载相应的VC
      [sb instantiateInitialViewController];(左侧小箭头的初始VC)
      [sb instantiateViewControllerWithIdentifier:@"VCName"];
    

    相比较xib,sb不仅可以处理VC间跳转,同时对TableView等视图可以直接拖入Cell进行布局,xib更倾向view层面,场景复杂,sb更倾向VC层面,功能强大。

    Xib相关

    • nib文件加载的两各方法
    [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
    [[UINib nibWithNibName:@"" bundle:nil]instantiateWithOwner:nil options:nil];
    
    • TableView自定义Cell时,控件是添加到它的contentView上,不能直接添加到cell上,否则某些情况下就会出问题,如左滑删除cell时。

    • TableView中Cell 有重用机制,当从重用池中取出cell时,上面的数据并没有清空,再次赋值时遗漏某个控件则可能出现显示错误。
      可以实现下面的方法,将cell上控件赋值清空。

    - (void)prepareForReuse;
    
    • 使用Asset Catalog 完全不用关心图片名称,也不用按照@1x,@2x和@3x 的规则命名,不用关心存储位置,便于管理图片。

    • AutoLayout 本质上也是用唯一性来设置视图的frame。当view使用了AutoLayout布局,系统会在layoutSubViews时加载它的约束信息。所以如果在viewDidLoad中设置frame起作用后,紧接着执行layoutSubViews中就被覆盖掉了。

      可以将控件的某个约束拉成属性,并动态调整改变。

         [UIView animateWithDuration:0.2 animations:^{
             self.widthConstraint.constant = 100;
             self.heightConstraint.constant = 100;
             //添加动画
             [self.view layoutIfNeeded];
         }];
    
    • 在iOS 9之后推出的UIStackView容器视图,在有些布局中特别高效,但由于版本的限制,所以可以用ForkingDog团队开源的FDStackView,在低版本中使用高版本控件。只要在Show the File inspector中将Builds for 指琛为最高的iOS 版本

    • UIScrollView 属于AutoLayout 中的异类,约束稍显麻烦。
      在给视图添加相对于父View的约束时,并不是以父view的尺寸为依据,而是以父view内容的尺寸为依据。
      普通View 不能滚动,所以它的尺寸就是内容尺寸。
      ScrollView的内容尺寸也就是其contentSize必须确定,才能准确布局子视图。
      一般有两种处理方式:

      1. 给scrollView添加一个容器View,给容器view添加必要的约束以确定contentSize。再向容器视图中添加子视图即可。
      2. 直接给scrollView添加子视图,但需要添加足够的约束,以保证可以唯一的确定它的contentSize.

    XIB 嵌套

    关于xib的嵌套使用在父视图-awakeFromNib方法中用代码添加子视图,但不优雅。

    设置Files Owner 清空View的Classs.jpg
    • 同时ScrollowView重写方法
    //设置frame时用self的尺寸,而不要随意设置(很重要)
    - (id)initWithCoder:(NSCoder *)aDecoder{
        if (self = [super initWithCoder:aDecoder]) {
            UIView *containerView = [[[UINib nibWithNibName:@"ScrollowView" bundle:nil] instantiateWithOwner:self options:nil] objectAtIndex:0];
            CGRect newFrame = CGRectMake(0, 0, self.frame.size.width, 179);
            containerView.frame = newFrame;
            [self addSubview:containerView];
        }
        return self;
    }
    
    • 在父视图上添加UIView,设置其类型为ScrollowView

    Storyboard

    • 在IB 中选择View Controller,在 Show the Size inspector 中有 Simulated Size标签,该标签两个值:Fixed 和 Freeform 。可以设置为Freeform改变VC的Size,它影响在IB 中的显示 。这在ScrollView布局中非常有用。

    • segue 进行页面间跳转很方便,segue可以设置Identifier

    //跳转时传值
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
        
        if ([segue.identifier isEqualToString:@"showSec"]) {
            if ([segue.destinationViewController isKindOfClass:[SecondViewController class]] && [segue.sourceViewController isKindOfClass:[ViewController class]]) {
                SecondViewController *secVC = segue.destinationViewController;
                //传值建议用KVC
                //不能直接对目标VC的UI进行更改
                [secVC setValue:@"不是测试" forKey:@"secStr"];
                
            }
        }
    }
    
    //根据相应条件判断是否要跳转
    - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
        
        if ([identifier isEqualToString:@"showSec"]) {
            //根据condition判断
            //返回值决定是否跳转
            return YES;
        }else{
            
            return NO;
        }
        
    }
    

    回传时须要在要返回去的VC中实现(IBAction)方法,参数UIStoryboard。将返回事件联接到VC 顶部的Exit按钮上,会自动选择方法。

    //回传值的方法
    - (IBAction)theBack:(UIStoryboardSegue*)segue{
       
        //注意
        //返回值必须是 IBAction
        //方法名随意
        
        if ([segue.identifier isEqualToString:@"backViewController"]) {
            
            //destinationViewController 是当前VC
            NSLog(@"%@",[segue.destinationViewController class]);
            
            //sourceViewController 是源VC
            SecondViewController *secVC = segue.sourceViewController;
            NSLog(@"%@",secVC.backStr);
        }
    }
    
    • 用代码将一个VC中的视图添加到另一个VC中时,通常会这样写
    [self.view addSubView:subView];
    [self  addChildViewController:suVC];
    

    在IB 中系统提供一个ContainerView 专门处理这类事件,需要时可尝试使用。

    • TableView中点击cell 或 accessory 跳转,为方便传值,可以实现
    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
          UITableViewCell  *cell = sender;
          NSIndexPath *index =  [self.tableView indexPathForCell:cell];
    }
    
    • SB 中有需要设置静太Cell 的需求,cell 样式简单,数量较少。则可以通过创建 TableViewController ,选中TableView 在 show the Attributes inspector 中将Content标签设置成Static Cells即可。
      代理方法都不用写,cell 的 identifire 也不用设置
      TableViewController可以与源文件关联,但静态Cell 不可有关联源文件,只能是UITableViewCell
    • SB中CollectionView 设置headView 和footerView 除了勾选 SectionHeader 和 SectionFooter ,还要实现代理方法
    - (UICollectionReusableView*)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath{
        if (kind == UICollectionElementKindSectionHeader) {
            return [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:@"Header" forIndexPath:indexPath];
        }else{
            return [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:@"Footer" forIndexPath:indexPath];
        }
    }
    

    Use Trait Variations适配

    通过底部的Trait Variations 特征亦是适配横竖屏下的不同约束。
    点击相应约束,constant侧面有“+”标志的可以增加以添加不同特征约束。

    设备 竖屏 横屏
    3.5英寸 iPhone wC hR wC hC
    4.0英寸 iPhone wC hR wC hC
    4.7英寸 iPhone wC hR wC hC
    5.5英寸 iPhone wC hR wR hC
    iPad(all) wR hR wR hR

    External Object 相关

    仅限 xib 使用

    1. 如图示做以下操作,并在 VC,VCView 和 VCManager 中拉相关点击事件。
    2. 在 VC 中代码
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        _manager = [VCManager new];
        //这里注意传入这个dic中的_aPersonHandle对象必须是全局变量
        NSDictionary *pramaDic = @{@"testKey" : _manager};
        NSDictionary *optionDic = @{UINibExternalObjects : pramaDic};
        //options这个参数只接收一个key为UINibExternalObjects的字典,
        //这个字典的value也必须是个字典,
        //这个value字典的key就是xib中设置的external object的identifier
        VCView *vcView = [[NSBundle mainBundle] loadNibNamed:@"VCView" owner:self options:optionDic][0];
      
        [self.view addSubview:vcView];
    }
    
    VCView Files Owner VCManager设置 Class Identifier

    Object [1]

    1. 新建ViewControllerManager 类,继承自NSObject,并实现 -(IBAction)***方法。
    2. IB中添加Object类,并且与源文件关联。
    3. 通过连线可以在ViewControllerManager中实现VC中部分代码逻辑。
    4. 可以将viewControllerManager作为属性拉到VC中去,也可以将VC作为属性拉到ViewControllermanager中去
      须要用weak修饰一下,防止循环引用 )

    VC瘦身的话可以考虑将VC中的Delegate中的内容放到Manager中去



    Xnip2018-09-26_10-49-04.jpg
    1. 实测仅SB成功

    相关文章

      网友评论

        本文标题:Interface Builder

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