你好,歡迎來到IOS教程網

 Ios教程網 >> IOS訊息 >> 關於IOS >> Interface Builder一些使用技巧

Interface Builder一些使用技巧

編輯:關於IOS

Interface Builder一些使用技巧

原文: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裡是這樣的:

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

Interface Builder一些使用技巧

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

Interface Builder一些使用技巧

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

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

Interface Builder一些使用技巧

消除副作用

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


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

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