SendBird為常見系統均提供了示例UI,方便開發者構建自己的聊天和短信功能。以前只有Objective-C的iOS示例UI,后來聽到諸多要求開發Swift版本的呼聲,于是我們將示例UI的語言從Objective-C轉換成了Swift。此過程中的大感受是:兩種語言確實存在不少差異。今天特意分享一些心得給大家,希望對你們有借鑒價值。
注意:示例UI并不使用Interface Builder(IB,Mac OS X平臺上用于設計和測試用戶界面的應用程序),而是從零開始構建的。所以以下范例不適用于使用Interface Builder的開發者。
SendBird示例UI可從Github庫下載。Objective-C和Swift語言項目均在同一個庫中,我們建議比較兩種代碼庫以更好理解其差異。
在iOS應用上實現UI就需要子類化UIView,也就是要重寫UIView的init方法。注意:兩種語言有所區別。
Objective-C只需在UIView子類中重寫必要的init方法。要初始化一個UIView框架,就要重寫initWithFrame:框架,如下所示:
@implementation SubUIView - (id) initWithFrame:(CGRect)frame
{ self = [super initWithFrame:frame]; if (self != nil) { // ... } return self;
} @end
然而Swift需要多一些步驟來重寫同一個init方法。首先,重寫使用CGRect框架作為其參數的init方法。根據UIView文檔,用Swift語言構建時,須重寫init(coder:),但我們不需要這種方法,就用如下代碼處理。類屬性初始化所需的代碼可以在init(frame:)中執行。
class SubUIView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
// ... }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
子類化UIViewController是iOS開發的重要步驟。使用Interface Builder的開發者需要重寫initWithNibName:bundle:,但既然我們用代碼來構建UI,就不需要執行這一方法了。只需重寫init方法,在其中初始化類屬性即可。
@implementation SubUIViewController - (id) init
{ self = [super init]; if (self != nil) { // ... } return self;
} @end
Swift也一樣要重寫init()方法。實現指定的初始化init(nibName:bundle:)來子類化UIViewController。重申:此過程不適用Interface Builder,所以無需定義nibName和bundle的值,而是調用比指定初始化更簡單的convenience初始化,將指定初始化init(nibName:bundle:)設為零。現在調用init()來初始化類,用重寫的(nibName:bundle:)執行類屬性。
class SubUIViewController: UIViewController {
convenience init() { self.init(nibName: nil, bundle: nil)
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) // Initialize properties of class }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
現在可以創建和調用UIViewController的子類,如下所示:
let viewController: SubUIViewController = SubUIViewController() self.navigationController?.pushViewController(viewController, animated: false)
沒有Interface Builder的情況下,就用Auto Layout中的NSLayoutConstraint類來設置View的大小和位置——注意Objective-C和Swift在這里有微妙差別。
Objective-C使用NSLayoutConstraint類中的constraintWithItem方法。
+ (instancetype)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c
Swift使用同一個類中的init方法。
convenience init(item view1: AnyObject, attribute attr1: NSLayoutAttribute,
relatedBy relation: NSLayoutRelation,
toItem view2: AnyObject?, attribute attr2: NSLayoutAttribute,
multiplier multiplier: CGFloat, constant c: CGFloat)
如果是Objective-C,則執行以下代碼。這段代碼將創建NSLayoutConstraint(定義self.profileImageView和self之間的位置),然后添加到self上。
[self addConstraint:[NSLayoutConstraint constraintWithItem:self.profileImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1 constant:kMessageCellLeftMargin]];
使用Swift也可以創建NSLayoutConstraint,具體如下:
self.addConstraint(NSLayoutConstraint.init(item: self.profileImageView!, attribute: NSLayoutAttribute.Leading, relatedBy: NSLayoutRelation.Equal, toItem: self, attribute: NSLayoutAttribute.Leading, multiplier: 1, constant: kMessageCellLeftMargin))
比較兩種語言版本你會發現,不同于Objective-C,Swift是從NSLayoutConstraint調用init方法的,而且屬性和relatedBy的枚舉值也有差別。
兩種語言NSLayoutConstraint中的枚舉值分別是:
typedef enum: NSInteger {
NSLayoutAttributeLeft = 1,
NSLayoutAttributeRight,
NSLayoutAttributeTop,
NSLayoutAttributeBottom,
NSLayoutAttributeLeading,
NSLayoutAttributeTrailing,
NSLayoutAttributeWidth,
NSLayoutAttributeHeight,
NSLayoutAttributeCenterX,
NSLayoutAttributeCenterY,
NSLayoutAttributeBaseline,
NSLayoutAttributeLastBaseline = NSLayoutAttributeBaseline,
NSLayoutAttributeFirstBaseline,
NSLayoutAttributeLeftMargin,
NSLayoutAttributeRightMargin,
NSLayoutAttributeTopMargin,
NSLayoutAttributeBottomMargin,
NSLayoutAttributeLeadingMargin,
NSLayoutAttributeTrailingMargin,
NSLayoutAttributeCenterXWithinMargins,
NSLayoutAttributeCenterYWithinMargins,
NSLayoutAttributeNotAnAttribute = 0 } NSLayoutAttribute;
enum NSLayoutAttribute : Int { case Left case Right case Top case Bottom case Leading case Trailing case Width case Height case CenterX case CenterY case Baseline static var LastBaseline: NSLayoutAttribute { get } case FirstBaseline case LeftMargin case RightMargin case TopMargin case BottomMargin case LeadingMargin case TrailingMargin case CenterXWithinMargins case CenterYWithinMargins case NotAnAttribute
}
enum {
NSLayoutRelationLessThanOrEqual = -1,
NSLayoutRelationEqual = 0,
NSLayoutRelationGreaterThanOrEqual = 1,
}; typedef NSInteger NSLayoutRelation;
enum NSLayoutRelation : Int { case LessThanOrEqual case Equal case GreaterThanOrEqual
}
使用UIButton、NSNotificationCenter、NSTimer等時,使用選擇器來分配要執行的方法。在Objective-C中,@selector指令代表使用選擇器。
- (void)test
{
// ... mTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerCallback:) userInfo:nil repeats:YES];
}
- (void)timerCallback:(NSTimer *)timer
{
// ... }
在Swift中,不需要使用指令或字符串來分配方法。
func test() {
// ... self.mTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "timerCallback:", userInfo: nil, repeats: true)
// ... }
func timerCallback(timer: NSTimer) {
// ... }
盡管在Swift代碼中也可以用Objective-C專門處理字符串的NSString,但要使用以String對象為屬性的UITextField上的文本或其他的話,就要清楚NSString和String的區別。
在Objective-C中,UITextField上的文本為NSString,所以屬性的長度就是字符串的長度。
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
NSString *message = [textField text]; if ([message length] > 0) {
// ... } return YES;
}
Swift是沒有長度屬性的,所以要用characters屬性的count屬性。
func textFieldShouldReturn(textField: UITextField) -> Bool {
let message: String = textField.text! if message.characters.count > 0 {
// ... } return true
}
在Objective-C中,我們用stringWithFormat:來創建一個格式化字符串。
[self.typingLabel setText:[NSString stringWithFormat:@"%d Typing something cool....", count]];
但在Swift中,String里沒有stringWithFormat方法,所以用init(format:_ arguments:)代之。我們可以分配一個與NSString格式化結構相同的格式化字符串來創建一個新字符串,然后給arguments賦以相關的值。
self.typingLabel?.text = String.init(format: "%d Typing something cool...", count)
就從數字格式上得到小和大值而言,Objective-C和Swift也有差別。Objective-C使用一個預定義宏來得到小和大值,但Swift則可以直接從數據類型上得到這些值。Objective-C使用的是如下的宏:
CGFLOAT_MAX
CGFLOAT_MIN
INT32_MAX
INT32_MIN
LLONG_MAX
LLONG_MIN
而Swift則從數據類型上得到小和大值,如下:
CGFloat.max CGFloat.min Int32.max Int32.min Int64.max Int64.min
Objective-C用NSDictionary來定義NSAttributedString的屬性。Swift則用Dictionary而不是NSDictionary,但想為Dictionary分配枚舉值的時候,做法稍有不同。
Objective-C直接為NSDictionary分配鍵值,如下所示:稱為NSUnderlineStyleSingle的枚舉值不能作為NSDictionary值直接分配,所以要先用@()將它轉換成一個對象。
NSDictionary *underlineAttribute = @{NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle)};
Swift可以直接為Dictionary分配鍵值(如下所示)。如果該值定義為AnyObject,那么Swift就跟Objective-C一樣不能直接使用枚舉值,而是使用rawValue屬性代之。
let underlineAttribute: [String: AnyObject] = [NSUnderlineStyleAttributeName: NSUnderlineStyle.StyleSingle.rawValue]
下表列出了SendBird示例UI項目語言轉換過程中所發現的Objective-C和Swift的其他差異。
如果決定使用Swift,建議先學習其基本知識,并試著將手頭現有的Objective-C項目轉化為Swift版本練練手。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。