WriteTyper - 在Mac上體驗(yàn)舊式打字機(jī)的觸感

NoisyTyper

首先得說(shuō)一下WriteTyper的前身NoisyTyper,一開(kāi)始我用的就是這個(gè)。

NoisyTyper

NoisyTyper是一款能夠讓你一遍打字一邊發(fā)出以前舊電影里老式打字機(jī)的聲音的Mac App,聲音相當(dāng)?shù)牟诲e(cuò),十分耐聽(tīng)。然而隨著時(shí)間的老去,系統(tǒng)的不斷更新,NoisyTyper開(kāi)始漸漸的力不從心,不再適用于當(dāng)前系統(tǒng)。于是乎,本君只好壓榨不多的時(shí)間,讓W(xué)riteTyper應(yīng)運(yùn)而生。

WriteTyper

NoisyTyper 是用OC寫的,為了順應(yīng)時(shí)代的召喚,我決定,參照NoisyTyper的代碼,用Swift 2.0來(lái)寫WriteTyper。對(duì)于Mac桌面應(yīng)用,小生第一次寫表示怕怕,現(xiàn)在其實(shí)沒(méi)那么多的不同,只要將UIxxx的思維轉(zhuǎn)換成NSxxx就已經(jīng)一只腳踏入大門了。

最后WriteTyper的成品如下圖,也就是說(shuō),我們要做的是一個(gè)menubar app而非window app。

WriteTyper

幾點(diǎn)筆記

申請(qǐng)權(quán)限:

    // システム環(huán)境設(shè)定に設(shè)定変更を依頼する
    func acquirePrivileges() -> Bool {
        let accessEnabled = AXIsProcessTrustedWithOptions(
            [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true])
        if !accessEnabled {
            print("You need to enable the WriteTyper in the System Prefrences")
        }
        return accessEnabled
    }

顯示/隱藏Dock Icon:

func toggleDockIcon(showIcon state: Bool) -> Bool {
    var result: Bool
    if state {
        result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.Regular)
    }
    else {
        result = NSApp.setActivationPolicy(NSApplicationActivationPolicy.Accessory)
    }
    return result
}

全局鍵盤事件監(jiān)聽(tīng):

       if isAcquirePrivileges {
            NSEvent.addGlobalMonitorForEventsMatchingMask(
                NSEventMask.KeyDownMask, handler: {(theEvent: NSEvent) in
                    let key = theEvent.keyCode
                    self.playSoundsByKey(key)
            })
        } 

狀態(tài)欄上的pop view:

    let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(30)
    let popover = NSPopover()
    if let button = statusItem.button {
        button.image = NSImage(named: "typewriter")
        button.action = Selector("togglePopover:")
    }
    popover.contentViewController = SettingViewController(nibName: "SettingViewController", bundle: nil)
    ...

    func showPopover(sender: AnyObject?) {
        if let button = statusItem.button {
            popover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: .MinY)
        }
    }
    
    func closePopover(sender: AnyObject?) {
        popover.performClose(sender)
    }

來(lái)自 stackoverflow 的搬運(yùn)

下面設(shè)置開(kāi)機(jī)自啟的代碼來(lái)自stackoverflow,稍作修改,替換了過(guò)時(shí)的API:

    func applicationIsInStartUpItems() -> Bool {
        return (itemReferencesInLoginItems().existingReference != nil)
    }
    
    func itemReferencesInLoginItems() -> (existingReference: LSSharedFileListItemRef?, lastReference: LSSharedFileListItemRef?) {
        if let appUrl : NSURL = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
            let loginItemsRef = LSSharedFileListCreate(
                nil,
                kLSSharedFileListSessionLoginItems.takeRetainedValue(),
                nil
                ).takeRetainedValue() as LSSharedFileListRef?
            if loginItemsRef != nil {
                let loginItems: NSArray = LSSharedFileListCopySnapshot(loginItemsRef, nil).takeRetainedValue() as NSArray
                //print("There are \(loginItems.count) login items")
                let lastItemRef: LSSharedFileListItemRef = loginItems.lastObject as! LSSharedFileListItemRef
                for var i = 0; i < loginItems.count; ++i {
                    let currentItemRef: LSSharedFileListItemRef = loginItems.objectAtIndex(i) as! LSSharedFileListItemRef
                    
                    if let resUrl = LSSharedFileListItemCopyResolvedURL(currentItemRef, 0, nil){
                        let urlRef: NSURL = resUrl.takeRetainedValue()
                        //print("URL Ref: \(urlRef.lastPathComponent!)")
                        if urlRef.isEqual(appUrl) {
                            return (currentItemRef, lastItemRef)
                        }
                    } else {
                        //print("Unknown login application")
                    }
                }
                //The application was not found in the startup list
                return (nil, lastItemRef)
            }
        }
        return (nil, nil)
    }
    
    func toggleLaunchAtStartup() {
        let itemReferences = itemReferencesInLoginItems()
        let shouldBeToggled = (itemReferences.existingReference == nil)
        let loginItemsRef = LSSharedFileListCreate(
            nil,
            kLSSharedFileListSessionLoginItems.takeRetainedValue(),
            nil
            ).takeRetainedValue() as LSSharedFileListRef?
        if loginItemsRef != nil {
            if shouldBeToggled {
                if let appUrl : CFURLRef = NSURL.fileURLWithPath(NSBundle.mainBundle().bundlePath) {
                    LSSharedFileListInsertItemURL(
                        loginItemsRef,
                        itemReferences.lastReference,
                        nil,
                        nil,
                        appUrl,
                        nil,
                        nil
                    )
                    print("Application was added to login items")
                }
            } else {
                if let itemRef = itemReferences.existingReference {
                    LSSharedFileListItemRemove(loginItemsRef,itemRef);
                    print("Application was removed from login items")
                }
            }
        }
    }

refs: http://stackoverflow.com/questions/26475008/swift-getting-a-mac-app-to-launch-on-startup

絲滑般的快感

本文就是伴著 WriteTyper 一字一字發(fā)出的老式打字機(jī)的聲音寫下的,全文行云流水,酣暢淋漓,最后那感覺(jué)更不知與誰(shuí)言說(shuō)。總的來(lái)說(shuō),上手體驗(yàn)還是不錯(cuò)的,對(duì)于經(jīng)常碼字的同志,我相信,這會(huì)是提高生產(chǎn)力的必備神器。

最后點(diǎn)評(píng),每當(dāng)別人使用嘈雜不已的機(jī)械鍵盤渾然天成時(shí),你應(yīng)該默默的拿出本本,用打字機(jī)的聲音和他一干到底。

歡迎前往下載與Star。
官網(wǎng):https://urinx.github.io/app/writetyper
Github:https://github.com/Urinx/WriteTyper

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容