OSX 开发知识点汇总(三)

想第一时间获取我的最新文章,请关注公众号: 技术特工队

NSStackView

OSX 中 NSStackView 使用自动布局(系统的自动布局特性)来根据你的要求管理和对齐一组视图。可以设置为 水平方向,或者垂直方向。如下:

1
2
stackView.orientation = .horizontal //水平方向
stackView.orientation = .vertical //垂直方向

添加子view

为stackView 中添加 子view的方式如下:

1
stackView.addArrangedSubview(view)

注意: 由于stackView 是继承自 NSView, 所有也会有 addSubview(view) 的方法,但是调用此方法,不会自动扩大 stackView 的宽高,而导致新加的view 显示不出来。

移除子view

移除子 View 采用如下方式:

1
2
stackView.removeArrangedSubview(view) 
view.removeFromSuperview()

注意:子view从stackView移除后,还必须将子view从父view中移除

指定添加的每个View的间距

1
2
3
4
5
//指定每个添加的每个View间的间距
stackView.spacing = 5

// 在customerView 后设置space为10
stackView.setCustomSpacing(10, customerView)

参考资料:
https://swift.gg/2016/03/31/ios9-uistackview-guide-swift/

OSX坐标系翻转

这里为什么要说坐标系呢? 因为在 iOS等其他移动设备上远点都左上角,而在OSX中坐标原点在左下角,这导致在有些 iOS 库想兼容到 OSX就要做很多的适配的问题。然而系统提供了坐标翻转的函数,可以很方便的将 OSX 坐标原点转化为 左上角,保持与iOS一致,在使用的NSView中复写如下方法:

1
2
3
- (BOOL)isFlipped {
return YES;
}

http://www.macdev.io/ebook/nsview.html

Swift 倒序循环

1
2
3
4
5
for i in (0..<3).reversed()
//表示:2,1,0

for i in stride(from:3, through:0, by:-1)
//表示3,2,1,0

NSWindow

NSApp.keyWindow

表示当前正在接受键盘事件的窗口,比如一个应用有多个窗口,当前窗口正在接受键盘事件或状态栏未灰掉的当前的窗口为 keyWindow, 官方文档对返回值解释如下:

1
2
The value of this property is nil when there is no window receiving keyboard events. 
The property might be nil because the app’s storyboard file has not yet finished loading or when the receiver is not active.

也就是说如果如果没有窗口接收键盘事件或者storyboard文件还未加载完,或窗口未激活,将返回的都是 nil。

NSApp.mainWindow

表示一个应用程序的主Window,官方的返回值说明如下:

1
The value in this property is nil when the app’s storyboard or nib file has not yet finished loading. It might also be nil when the app is inactive or hidden.

当app的 storyboard 或者 xib文件未加载完成,或者当前窗口为未激活状态或者是隐藏状态时,将返回nil。

注意: 所以项目中需要使用mainWindow 或者keyWinodw 时需要注意为空的情况。

https://zonble.net/archives/2010_08/1352.php

window 窗口最小化

1
2
3
4
5
6
7
8
// 使当前的窗口最小化
window.miniaturize(self)
// 使当前的窗口恢复原来状态取消最小化
window.deminiaturize(self)
// 如果当前为最小化则最大化,如果当前为最大化则最小化,取反。
window.toggleFullScreen(nil)
// 判断是否为全屏的状态
let isFullScreen = window.styleMask.contains(.fullScreen)

window storyboard 代码加载

1
2
3
4
let mainStoryboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "main"), bundle: nil)
// 使用mainStoryboard自动连线控制的NSWindowController,
let mainWindowController = mainStoryboard.instantiateInitialController() as! MainWindowController
mainWindowController.showWindow(nil) // 显示出来。

如果storyboard 不是我们默认启动,需要代码启动的化,上面需要我们自行定义一个NSWindow的子类MainWindowController,在初始化完成后强制转化为 MainWindowController

如果已经有一个window启动,需要加载storyboard中的某个viewControl进行替换。可以使用下面的语句进行初始化:

1
2
3
4
5
let mainStoryboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "main"), bundle: nil)
//得到指定的storyboard中指定的ViewController。
let mainViewController = mainStoryboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "InMeetingViewController")) as? NSViewController
// 将新创建的viewController 赋值给已经存在的Window中的contentViewController。
otherWindowController.contentViewController = mainViewController

objc_setAssociatedObject/objc_getAssociatedObject

这两个方式是oc中方法,主要功能是什么呢?。在 oc 中可以使用 Category可以对一些系统类进行添加方法,相当于 Swift 中的 extension的概念,但是我们常用有需求希望能够对一个类中添加一个属性,经常是很难弄,但是 oc 就很好的提供了一个方法 objc_setAssociatedObject 来对一个类进行属性的添加扩展。这是一个运行时修改的库,
所以必须要先引入 objc/runtime.h

objc_setAssociatedObject

具体如下:

1
2
3
4
5
// object 为需要添加属性的类
// key 为保证对象级别唯一的常量
// value 为object 对所添加对象的值
// policy 为关联的策略。
public func objc_setAssociatedObject(_ object: Any, _ key: UnsafeRawPointer, _ value: Any?, _ policy: objc_AssociationPolicy)

关联的策略就像是在 oc 中自定义 property 的引用类型一样。具体对应的类型如下:

策略 等价属性 说明
OBJC_ASSOCIATION_ASSIGN @property (assign) or @property (unsafe_unretained) 弱引用关联对象
OBJC_ASSOCIATION_RETAIN_NONATOMIC @property (strong, nonatomic) 强引用关联对象,且为非原子操作
OBJC_ASSOCIATION_COPY_NONATOMIC @property (copy, nonatomic) 复制关联对象,且为非原子操作
OBJC_ASSOCIATION_RETAIN @property (strong, atomic) 强引用关联对象,且为原子操作
OBJC_ASSOCIATION_COPY @property (copy, atomic) 复制关联对象,且为原子操作

实例如下对 NSApp 添加一个属性值为test:

1
2
3
// 执行一个对象变量,后面使用该对象的地址。
var keyMainString:UInt8 = 0
objc_setAssociatedObject(NSApplication.shared, &keyMainString, "test", .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

objc_getAssociatedObject

上面添加了相应的属性,下面则要获取添加的属性 objc_getAssociatedObject

1
2
let value = objc_getAssociatedObject(NSApplication.shared, &keyMainString)
// 如果上面进行了设置,那么这里将获得的值为“test”

通过上面两个方法就可以对系统类添加一个任意的属性值,和获取系统类的添加的属性值,对了,如果要移除关联的对象,可以对上面设置的值重新设置为 nil 即可。
当然还有一个 objc_removeAssociatedObjects,这样可以把某个对象所设置的所有关联属性全部移除掉,一般都不会这样做,避免引起其他异常。

参考资料:
http://blog.leichunfeng.com/blog/2015/06/26/objective-c-associated-objects-implementation-principle/
https://www.jianshu.com/p/7f7255d4e76d

WangXin wechat
欢迎订阅我的微信公众号,第一时间获取最新文章!
坚持原创技术分享,您的支持将鼓励我继续创作!