你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Interface Builder一些使用技巧

Interface Builder一些使用技巧

編輯:IOS開發基礎

What-did-u-learn-today.jpg

本文由CocoaChina譯者王小北(CC論壇ID)翻譯
原文:Tips for Interface Builder


當年輕的開發者朋友們寫了一大堆的代碼時,他們會覺得很高興,並且喜歡誇耀自己寫的項目有成千上萬行代碼。

隨著經驗越來越豐富,我們意識到代碼是昂貴的,因為要花很多時間去維護、調試和尋找原因。

這也是為什麼我們應該使用工具來幫助我們避免寫出樣板代碼。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裡是這樣的:

replacement.png

這個方法有一個簡單的實現,下面的是一個最“復雜”的:

- (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了,將老且脆弱的運行時屬性:

old_runtime_attributes.png

修改成了一個可編輯的屬性,我們再也不會拼寫錯誤了:

inspectable.png

可支持的屬性有下列這些類型:

  • 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增加了一個漂亮的邊界顏色編輯器

border_color.png

消除副作用

給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,不是一個只能二選一的問題。


  • 本文僅用於學習和交流目的,轉載請注明文章譯者、出處以及本文鏈接。

  • 感謝博文視點對本期翻譯活動的支持

博文logo-01.jpg

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved