一种标准化的层次结构,保存了可执行代码及代码所需要的资源。
Next Interface Builder
xib -> sb -> AutoLayout -> LaunchScreen.storyboard
苹果越来越重视 IB。
# 1 最新版本
pod 'AFNetworking'
# 2 最新的2.x版本
pod 'AFNetworking', '~>2.5.3'
# 3 指定版本
pod 'AFNetworking', '2.5.3'
试用
$ pod try AFNetworking
# master
pod 'Alamofile', :git => 'https://github.com/Alamofile/Alamofile.git'
# branch
pod 'Alamofile', :git => 'https://github.com/Alamofile/Alamofile.git', :branch -> 'dev'
# commit
pod 'Alamofile', :git => 'https://github.com/Alamofile/Alamofile.git', :commit => '0f506b1c45'
pod 'AFNetworking', :path => '../externals/libs/AFNetworking'
pod 'AFNetworking', :podspec => '../externals/libs/AFNetworking.podspec'
“:podspec=>”用于指定本地的一个xxx.podspec文件。
$ pod init
$ cat Podfile
Open As -> Source Code
<<<<<<<
=======
>>>>>>>
编辑好,再删除这三行就可以了。
如果用 Source Code 不能打开,就用文件编辑器(vim, etc)打开,把systemVersion等冲突解决。
连线
mvc
override func loadView() {
self.view = CustomView.init(frame: UIScreen.main.bounds)
}
xib 既可以与 UIView 关联,也可以与 UIViewController 关联,也可以同时关联 UIView 与 UIViewController
- (NSArray *)loadNibNamed:(NSString *)name
owner:(id)owner
options:(NSDictionary *)options;
func loadNibNamed(_ name: String,
owner: Any?,
options: [AnyHashable : Any]? = nil) -> [Any]?
Parameters 参数
let testView = Bundle.main.loadNibNamed("TestView", owner: nil, options: nil)?[0] as! UIView
view.addSubview(testView)
NS_CLASS_AVAILABLE_IOS(4_0) @interface UINib : NSObject
// If the bundle parameter is nil, the main bundle is used.
// Releases resources in response to memory pressure (e.g. memory warning), reloading from the bundle when necessary.
+ (UINib *)nibWithNibName:(NSString *)name bundle:(nullable NSBundle *)bundleOrNil;
// If the bundle parameter is nil, the main bundle is used.
+ (UINib *)nibWithData:(NSData *)data bundle:(nullable NSBundle *)bundleOrNil;
// Returns an array containing the top-level objects from the NIB.
// The owner and options parameters may both be nil.
// If the owner parameter is nil, connections to File's Owner are not permitted.
// Options are identical to the options specified with -[NSBundle loadNibNamed:owner:options:]
- (NSArray *)instantiateWithOwner:(nullable id)ownerOrNil options:(nullable NSDictionary *)optionsOrNil;
@end
@available(iOS 4.0, *)
open class UINib : NSObject {
// If the bundle parameter is nil, the main bundle is used.
// Releases resources in response to memory pressure (e.g. memory warning), reloading from the bundle when necessary.
public /*not inherited*/ init(nibName name: String, bundle bundleOrNil: Bundle?)
// If the bundle parameter is nil, the main bundle is used.
public /*not inherited*/ init(data: Data, bundle bundleOrNil: Bundle?)
// Returns an array containing the top-level objects from the NIB.
// The owner and options parameters may both be nil.
// If the owner parameter is nil, connections to File's Owner are not permitted.
// Options are identical to the options specified with -[NSBundle loadNibNamed:owner:options:]
open func instantiate(withOwner ownerOrNil: Any?, options optionsOrNil: [AnyHashable : Any]? = nil) -> [Any]
}
let testViewNib = UINib.init(nibName: "TestView", bundle: Bundle.main)
func loadTestView() {
let testView = UINib.instantiate(withOwner: nil, options: nil)[0] as! UIView
view.addSubview(testView)
}
let homeVC = HomeViewController()
class HomeViewController {
var aName: String?
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: "HomeController", bundle: nil)
}
init(aName: String) {
name = aName
super.init(nibName: "HomeController", bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
嵌套 xib
https://github.com/iOSDevLog/iOSDevLog/tree/master/228.%20NestedXib
图片上传失败请。参考 http://iosdevlog.com/ios/2017/12/19/ios-ui-interface-builder.html
@available(iOS 5.0, *)
open class UIStoryboard : NSObject {
public /*not inherited*/ init(name: String, bundle storyboardBundleOrNil: Bundle?)
open func instantiateInitialViewController() -> UIViewController?
open func instantiateViewController(withIdentifier identifier: String) -> UIViewController
}
storyboard Demo
extension UIViewController {
class func storyboardID() -> String {
return String(describing: self)
}
}
let userStoryBoard = UIStoryboard(name: "User", bundle: nil)
let profileVC = userStoryBoard.instantiateInitialViewController(withIdentifier: ProfileController.storyboardID())
Autoresizing
默认选中左,上。
对应代码为:
testView.autoresizingMask = [.flexibleRightMargin, .flexibleBottomMargin]
外框的 上、下、左、右如果选中,则UIView的边框与父View边框距离保持不变。
中间带箭头的选中表示UIView边框是随屏幕尺寸变化的。 否则UIView大小保持不变。
也可以看右侧的动画查看显示效果。
item1.attribute1 = multiplier * item2.attribute2 + constant
public enum NSLayoutAttribute : Int {
case left
case right
case top
case bottom
case leading
case trailing
case width
case height
case centerX
case centerY
case lastBaseline
@available(iOS 8.0, *)
case firstBaseline
@available(iOS 8.0, *)
case leftMargin
@available(iOS 8.0, *)
case rightMargin
@available(iOS 8.0, *)
case topMargin
@available(iOS 8.0, *)
case bottomMargin
@available(iOS 8.0, *)
case leadingMargin
@available(iOS 8.0, *)
case trailingMargin
@available(iOS 8.0, *)
case centerXWithinMargins
@available(iOS 8.0, *)
case centerYWithinMargins
case notAnAttribute
}
public enum NSLayoutRelation : Int {
case lessThanOrEqual
case equal
case greaterThanOrEqual
}
public struct UILayoutPriority : RawRepresentable, Equatable, Hashable {
public init(_ rawValue: Float)
public init(rawValue: Float)
}
extension UILayoutPriority {
@available(iOS 6.0, *)
public static let required: UILayoutPriority
@available(iOS 6.0, *)
public static let defaultHigh: UILayoutPriority // This is the priority level with which a button resists compressing its content.
@available(iOS 6.0, *)
public static let defaultLow: UILayoutPriority // This is the priority level at which a button hugs its contents horizontally.
@available(iOS 6.0, *)
public static let fittingSizeLevel: UILayoutPriority // When you send -[UIView systemLayoutSizeFittingSize:], the size fitting most closely to the target size (the argument) is computed. UILayoutPriorityFittingSizeLevel is the priority level with which the view wants to conform to the target size in that computation. It's quite low. It is generally not appropriate to make a constraint at exactly this priority. You want to be higher or
lower.
}
默认设置了 Width 和 Height
Axis
Alignment
Distribution
Space
Baseline Relative
UIScrollView 的子 View 需要设置 6 个约束
scrollView.contentSize.width = subView.leading + subView.width + subView.trailing;
scrollView.contentSize.height = subView.top + subView.height+ subView.bottom;
设置 ScrollView 的子 View 约束时一定要让系统确定 ScrollView 的 contentSize。
https://github.com/iOSDevLog/iOSDevLog/tree/master/229.%20ScreenLunch
@interface AppDelegate ()
@property (nonatomic, strong) UIWindow* launchWindow;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.launchWindow = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds];
self.launchWindow.hidden = NO;
self.launchWindow.windowLevel = UIWindowLevelNormal + 1;
self.launchWindow.rootViewController = [UIStoryboard storyboardWithName:@"Launch Screen" bundle:nil].instantiateInitialViewController;
[UIView animateWithDuration:3 delay:0.5 options: UIViewAnimationOptionCurveEaseInOut animations:^{
self.launchWindow.alpha = 0;
} completion:^(BOOL finished) {
self.launchWindow.hidden = YES;
self.launchWindow.windowLevel = UIWindowLevelNormal - 1;
self.launchWindow.alpha = 1;
}];
return YES;
}
@end
var launchWindow: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
launchWindow = UIWindow(frame: UIScreen.main.bounds)
launchWindow?.windowLevel = UIWindowLevelNormal + 1
launchWindow?.isHidden = false
launchWindow?.rootViewController = UIStoryboard(name: "LaunchScreen", bundle: nil).instantiateInitialViewController()
UIView.animate(withDuration: 0.5, delay: 3, options: .curveEaseInOut, animations: {
self.launchWindow?.alpha = 0
}) { (finish) in
if finish {
self.launchWindow?.isHidden = true
self.launchWindow?.windowLevel = UIWindowLevelNormal - 1
self.launchWindow?.alpha = 1
}
}
return true
}
设备 | 竖屏 | 横屏 |
---|---|---|
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 | wR hR | wR hR |
IB 中的类型 | Swift | Objective-C |
---|---|---|
Boolean | Bool | BOOL |
Number | + | + |
String | String | NSString |
Localized String | String | NSString |
Point | CGPoint | CGPoint |
Size | CGSize | CGSize |
Rect | CGRect | CGRect |
Range | Range | NSRange |
Color | UIColor | UIColor |
Image | UIImage | UIImage |
Nil | Nil | Nil |
Number 在 Swift 里面可以对应 Int、Double、Float。 在 Objective-C 里面可以对应 NSInteger、NSNumber 等。
extension UIView {
var borderColor: UIColor {
set {
self.layer.borderColor = newValue.cgColor
}
get {
return UIColor.init(cgColor: self.layer.borderColor!)
}
}
}
Bundle 和 UINib
两种策略
Base
Localizable.strings(English)
"test" = "hello world";
Localizable.strings(Chinese(Simplified))
"test" = "你好,世界";
override func viewDidLoad() {
super.viewDidLoad()
// Localizable.string
testLabel.text = NSLocalizedString("test", comment: "")
// Home.strings
// testLabel.text = NSLocalizedString("test", tableName: "Home", comment: "")
}
新建 InfoPlist.strings,在 Show the File inspector 点击 Localize...
InfoPlist.strings(English)
CFBundleName = "hello world";
CFBundleDisplayName = "hello world";
InfoPlist.strings(Chinese(Simplified))
CFBundleName = "你好,世界";
CFBundleDisplayName = "你好,世界";
Localizable.strings(English)
"testImageName" = "1";
Localizable.strings(Chinese(Simplified))
"testImageName" = "2";
testImageView.image = UIImage.init(named: NSLocalizedString("testImageName", comment: "")
选中图片,Show the File inspector 点击 localize...,替换 zh-Hans.lproj 中的资源文件。
https://github.com/iOSDevLog/iOSDevLog/tree/master/230.%20Localizations
extension Bundle {
class func loadLocalizableString(languageBundleName: String, key: String) -> String? {
let languageBundlePath = Bundle.main.path(forResource: languageBundleName, ofType: "lproj")
guard languageBundlePath != nil else {
return nil
}
let languageBundle = Bundle.init(path: languageBundlePath!)
guard languageBundle != nil else {
return nil
}
let value = languageBundle?.localizedString(forKey: key, value: "", table: "")
return value
}
}
func changeLanguage() {
let kTestKey = "testKey"
switch selectIndex {
case 0:
testLabel.text = Bundle.loadLocalizableString(languageBundleName: Language.simplifiedChinese.rawValue, key: kTestKey)
break
case 1:
testLabel.text = Bundle.loadLocalizableString(languageBundleName: Language.english.rawValue, key: kTestKey)
break
case 2:
testLabel.text = Bundle.loadLocalizableString(languageBundleName: Language.japanese.rawValue, key: kTestKey)
break
case 3:
testLabel.text = Bundle.loadLocalizableString(languageBundleName: Language.korea.rawValue, key: kTestKey)
break
default:
break
}
}
使用 RBStoryboardLink https://github.com/rob-brown/RBStoryboardLink
代码量庞大、结构臃肿、可维护性差的 VC。
只能在于 xib
Swift
Objective-C
可以不运行程序的情况下把源文件中的一些代码实时渲染到 IB 中,但是源文件必须是 UIView 或者 NSView 的子类。
prepareForInterfaceBuild()
只需要将实时渲染的代码放到 prepareForInterfaceBuild() 方法中就可以了,该方法并不会在程序运行时调用。
用 @IBInspectable
修饰的属性会显示在 IB 的 Show the Attributes inspector。
extension UIView {
private struct AssociatedKeys {
static var name: String?
}
// 存储属性
@IBInspectable var name: String {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.name) as! String
}
set {
objc_setAssociatedObject(self, &AssociatedKeys.name, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
// 计算属性
@IBInspectable var borderColor: UIColor {
set {
self.layer.borderColor = newValue.cgColor
}
get {
return UIColor.init(cgColor: self.layer.borderColor!)
}
}
}
comment
+ =
选中 View, 按住 option
,悬停在其它 View 上。
Editor -> Guides -> Add Horizontal Line
command
+ -
Editor -> Guides -> Add Vertical Line
command
+ ctrl
+ |
command
+ shift
+ right click
连线
小技巧两个窗口
command
+ c
command
+ v
最好的做法就是重启 Xcode。
IB 文件是否与源文件关联
先在源文件中定义好方法,再从源文件 拖 到 IB 文件进行 连线