热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

InterfaceBuilder一些使用技巧

当年轻的开发者朋友们写了一大堆的代码时,他们会觉得很高兴,并且喜欢夸耀自己写的项目有成千上万行代码。随着经验越来越丰富,我们意识到代码是昂


当年轻的开发者朋友们写了一大堆的代码时,他们会觉得很高兴,并且喜欢夸耀自己写的项目有成千上万行代码。


随着经验越来越丰富,我们意识到代码是昂贵的,因为要花很多时间去维护、调试和寻找原因。


这也是为什么我们应该使用工具来帮助我们避免写出样板代码。Interface Builder就是这些工具中的一个。


下面说一些我喜欢的小提示和技巧。


Storyboards还是Xib


很显然,苹果是极力推荐我们使用storyboard的,因为下列的一些特性只在storyboard中才有:



  • Prototype cells
  • Segues
  • Container View Controllers


很多人抱怨在团队合作的时候使用storyboard很困难,因为在合并代码的时候会有冲突。以前这个问题是会经常出现,但是现在storyboard的格式更好、更容易理解了。


即使这样,我也不喜欢干合并代码的活。不过幸运的是,当我们使用多个storyboard合理地划分项目时,就可以避免很多潜在的冲突。


我比较喜欢给每一个场景创建一个storyboard,例如最新的那个app:



  • 入职 — 新建账户以及初始的信息界面
  • 新建流 — 当用户可以新建内容时
  • 活动 — 查看提到自己、自己或其他用户的活动的相关界面


iOS 9给我们介绍了Storyboard References,使我们能够更容易地使用多个storyboard来分割应用。


如果需要部署在老版本的iOS target上呢?


玩转Storyboard, Xib或类引用


如果你运气不够好,未能使用iOS9作为最低的部署目标,仍旧可以用你自己的方法实现。虽然需要花费点功夫,但这些努力都是值得的。多年以来,我一直在使用一个非常简单的扩展去拥有我自己的Storyboard, xib 和 类引用。


扩展的主要内容:



  • 在storyboard中新建一个空的UIViewController
  • 在UIViewController中用IBInspectable扩展来支持自定义的replacementIdentifier图式


storyboard.StoryboardName.ControllerIdentifier


storyboard.StoryboardName -> loads the initial vc


class.name


xib.name


@interface UIViewController (KZReplacement)

 

@property (nonatomic, copy) IBInspectable NSString *replacementIdentifier;

 

//! 如果设了replacementIdentifier,就新建一个controller,否则返回self

- (UIViewController *)kz_replacementController;

 

@end


在Interface Builder里是这样的:




这个方法有一个简单的实现,下面的是一个最“复杂”的:


 (UIViewController *)kz_replacementControllerWithIdentifier:(NSString *)identifier;

{

    NSDictionary *replacementSchemes = @{

            @"class" : [NSValue valueWithPointer:@selector(kz_createControllerWithClassIdentifier:)],

            @"storyboard" : [NSValue valueWithPointer:@selector(kz_loadControllerWithStoryboardIdentifier:)],

            @"xib" : [NSValue valueWithPointer:@selector(kz_loadControllerWithXibIdentifier:)]

    };

 

    __block UIViewController *replacement = nil;

    [replacementSchemes enumerateKeysAndObjectsUsingBlock:^(NSString *scheme, NSValue *selectorValue, BOOL *stop) {

        const struct _NSRange range = [identifier rangeOfString:scheme];

        if (range.location == 0)

        {

            *stop = YES;

            UIViewController *(*objc_msgSendTyped)(id, SEL, NSString *) = (UIViewController *(*)(id, SEL, NSString *))objc_msgSend;

            replacement = objc_msgSendTyped(self, selectorValue.pointerValue, [identifier substringFromIndex:range.length + 1]);

        }

    }];

 

    return replacement;

 

}


现在你只需在容器内的每一个VC执行这个方法。举个例子,如果你的app使用了TabBar,下面的方法对你每一个UITabBarController的子类都会起作用:


- (void)awakeFromNib;

{

    [super awakeFromNib];

    [super setViewControllers:[self processViewControllersByReplacingMocks:self.viewControllers] animated:NO];

}

 

- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated;

{

    [super setViewControllers:[self processViewControllersByReplacingMocks:viewControllers] animated:animated];

}

 

- (void)setViewControllers:(NSArray *)viewControllers;

{

    [super setViewControllers:[self processViewControllersByReplacingMocks:viewControllers]];

}

 

- (NSArray *)processViewControllersByReplacingMocks:(NSArray *)viewControllersAndMocks;

{

    NSMutableArray *array = [NSMutableArray new];

    [viewControllersAndMocks enumerateObjectsUsingBlock:^(UIViewController *viewController, NSUInteger idx, BOOL *stop) {

        UIViewController *replacement = [viewController kz_replacementController];

        [array addObject:replacement ?: viewController];

    }];

    return [array copy];

}


使用行为(Behaviours)


如果要给你们推荐一种模式的话,毫无疑问我会推荐“组合”(composition),这是个非常棒的模式,并且能自然而然地带来其他好的方面。


行为是一种组合,只能基于Interface Builder配置。


之前我在objc.io的架构指南中写过。


运行时属性和@IBInspectable


多年前我们就可以直接在IB内设置一些自定义属性了,但是还有很多人是不知道的。他们在Interface Builder中新建了一个UIView,然后再用代码来设置View的一些属性。


Xcode 6的运行时属性升级到支持@IBInspectable了,将老且脆弱的运行时属性:



修改成了一个可编辑的属性,我们再也不会拼写错误了:




可支持的属性有下列这些类型:



  • Int
  • CGFloat
  • Double
  • String
  • Bool
  • CGPoint
  • CGSize
  • CGRect
  • UIColor
  • UIImage


扩展UIKit


你还可以扩展现有的类型,比如UIKit中的类:


extension UIView {

    @IBInspectable var kz_borderColor: UIColor? {

        get {

            if let colorRef = layer.borderColor {

                return UIColor(CGColor: colorRef)

            }

 

            return nil

        }

        set {

            layer.borderColor = newValue?.CGColor

        }

    }

}


给UIView增加了一个漂亮的边界颜色编辑器




消除副作用


给NSLayoutConstraint增加一个标示符会咋样呢?iOS7增加了一个identifier字段作调试用,不过我在另外的一些地方使用了自定义的identifier。


我们的应用在不同的iOS设备的显示略有一些差异,不幸的是Size Classes未能满足这个特别的设计。


为了处理这个需求,我创建了一种简单的DSL语法来描述每一种设备的值:


KZ_SPEC(Create, (@{

  @"create.size.captionTextWidth" : kz_spec(260, 250, 234, 220),

  @"create.size.segmentCollectionView.height" : kz_spec(116, 116, 116, 116).offsetBy(16)

}))


并且扩展了NSLayoutConstraint,使我们能够为实际的界面绑定规格。这样就避免了在view中写样板代码了:


@interface NSLayoutConstraint(KZDesignSpec)

@property (nonatomic, copy) IBInspectable NSString* kz_specName;

@end

 

@implementation NSLayoutConstraint (KZDesignSpe)

static void const *kDesignSpecKey = &kDesignSpecKey;

 

- (NSString *)kz_specName;

{

  return objc_getAssociatedObject(self, kDesignSpecKey);

}

 

- (void)setKz_specName:(NSString *)kz_specName;

{

  objc_setAssociatedObject(self, kDesignSpecKey, kz_specName, OBJC_ASSOCIATION_COPY_NONATOMIC);

  self.constant = kz_floatForSpec(kz_specName);

  self.identifier = kz_specName;

}

@end


快捷键和隐藏选项


确认你知道自己的快捷键以及Interface Builder的一些隐藏选项。


我还记得在我使用了2年的IB后看见一个人使用Media Tab拉出一张图片,并创建了一个合适尺寸的UIImageView.而这时的我还是手动的……


快捷键有很多,这里给出了一些不错的。


当你在设置AutoLayout的时候,记得在拉约束的同时按Option键,或者使用编辑菜单。


总结


Interface Builder离完美还有很长的路,但它也远不是一些开发者认为的一个糟糕的工具。


很多应用不能说是“用Interface Builder来实现太复杂”了,这是很多人在我问他们为什么不用IB的时候的回答。


Foldify和Storest都大量地使用IB.


实际上,很多情况下使用IB足矣,特别是加上我之前给出的扩展,或者是你自己实现的扩展。


其他情况下使用代码。代码或IB,不是一个只能二选一的问题。


推荐阅读
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • Myappcrashedandthecodeisthefollowing:我的应用程序崩溃,代码如下:elseif(){CGDetailVie ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 序言Broadcast作为Android的四大组件之一,重要性不言而喻;一般我们使用广播的方式通常如下,继承BroadcastReceiver,新建一个广播类。publicclas ... [详细]
  • 20189216 2018-2019-2 《密码与安全新技术专题》第二次作业
    201892162018-2019-2《密码与安全新技术专题》第二次作业课程:《密码与安全新技术专题》班级:1892班姓名:鲍政李学号:20189216上课教师:谢四江上课日期: ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
author-avatar
梦的影子2502931765
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有