UserDefaults.didChangeNotification
应该在PreferencesViewController
里进行注册,而不是在需要变化的ViewController
里。因为如果是在后者注册,程序运行时,可能会出现意想不到的异常。即用户在没有打开设置的情况下,设置变了,而造成你的程序的异常。
今天下午排查了很久,最后发现是这个原因。
UserDefaults.didChangeNotification
应该在PreferencesViewController
里进行注册,而不是在需要变化的ViewController
里。因为如果是在后者注册,程序运行时,可能会出现意想不到的异常。即用户在没有打开设置的情况下,设置变了,而造成你的程序的异常。
今天下午排查了很久,最后发现是这个原因。
// MARK: - progress bar with file dealing process
private var counter:Int = 0
func process() {
// get total
// prepare alert
let screenFrame = NSScreen.main()!.frame
let window = NSWindow(contentRect: NSMakeRect(screenFrame.width / 2 - 140, screenFrame.height * 0.66 + 50, 280, 20),
styleMask: NSBorderlessWindowMask,
backing: .buffered, defer: false)
window.isOpaque = false
let alert = NSAlert()
alert.addButton(withTitle: "OK")
alert.buttons.first!.isHidden = true
// prepare prgreessBar
let progressBar = NSProgressIndicator(frame: NSMakeRect(0,0,296,20))
progressBar.isIndeterminate = false
progressBar.maxValue = 1000
progressBar.doubleValue = 0
let step = progressBar.maxValue / Double(total)
alert.accessoryView = progressBar
alert.messageText = "0/\(total)"
alert.beginSheetModal(for: window) { [unowned self] (buttonId) in
alert.window.orderOut(self)
}
let userInfo:[String:Any] = ["progress bar": progressBar, "step": step, "alert": alert, "total": total]
// timer to update progress bar UI
self.counter = 0
let timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(updateProgressbar(timer:)), userInfo: userInfo, repeats: true)
DispatchQueue.global().async() { [unowned self] () -> () in
func excute() {
// MARK: - do works
// for in loop
for _ in 0 ... total {
// update counter
self.counter += 1
}
}
excute()
DispatchQueue.main.async { [unowned self] () -> () in
timer.invalidate()
progressBar.doubleValue = 1000
alert.messageText = "\(total)/\(total)"
alert.buttons.first!.performClick(self)
}
}
}
func updateProgressbar(timer:Timer) {
DispatchQueue.main.async { [unowned self] () -> () in
if let dic = timer.userInfo as? [String:Any], let progressBar = dic["progress bar"] as? NSProgressIndicator, let step = dic["step"] as? Double, let alert = dic["alert"] as? NSAlert, let total = dic["total"] as? Int {
progressBar.doubleValue = step * Double(self.counter)
alert.messageText = "\(self.counter)/\(total)"
}
}
}
有趣的是,hidden
的按钮在程序里是可以点击的。
另外,必须通过模拟点击的方式才能正确退出alert
。采用其它方式有可能造成UI异常,甚至NSWindow
不能正确释放而导致程序退出时崩溃。
与预期的不同,URL
即使path
相同,URL
也可能是不同的。因此,应避免使用init(fileURLWithPath: String, isDirectory: Bool, relativeTo: URL?)
,使用appendingPathComponent(_:isDirectory:)
作为替代。
let url = URL(fileURLWithPath: "foo/bar", isDirectory: false)
let baseURL = url.deletingLastPathComponent()
let newURL = URL(fileURLWithPath: "bar", isDirectory: false, relativeTo: baseURL)
let testURL = baseURL.appendingPathComponent("bar")
print(url == newURL) // prints false
print(url.path == newURL.path) // prints true
print(url == testURL) // prints true
print(url.path == testURL.path) // prints true