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

NoisyTyper

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

NoisyTyper

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

WriteTyper

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

最后WriteTyper的成品如下圖亚隙,也就是說,我們要做的是一個(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)聽:

       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)
    }

來自 stackoverflow 的搬運(yùn)

下面設(shè)置開機(jī)自啟的代碼來自stackoverflow阿弃,稍作修改诊霹,替換了過時(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ī)的聲音寫下的,全文行云流水渣淳,酣暢淋漓脾还,最后那感覺更不知與誰言說∪肜ⅲ總的來說鄙漏,上手體驗(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)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市声离,隨后出現(xiàn)的幾起案子芒炼,更是在濱河造成了極大的恐慌,老刑警劉巖术徊,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件本刽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡赠涮,警方通過查閱死者的電腦和手機(jī)子寓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來笋除,“玉大人斜友,你說我怎么就攤上這事±” “怎么了鲜屏?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長国拇。 經(jīng)常有香客問我洛史,道長,這世上最難降的妖魔是什么贝奇? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任虹菲,我火速辦了婚禮,結(jié)果婚禮上掉瞳,老公的妹妹穿的比我還像新娘毕源。我一直安慰自己,他們只是感情好陕习,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布霎褐。 她就那樣靜靜地躺著,像睡著了一般该镣。 火紅的嫁衣襯著肌膚如雪冻璃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天损合,我揣著相機(jī)與錄音省艳,去河邊找鬼。 笑死嫁审,一個(gè)胖子當(dāng)著我的面吹牛跋炕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播律适,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼辐烂,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了捂贿?” 一聲冷哼從身側(cè)響起纠修,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎厂僧,沒想到半個(gè)月后扣草,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颜屠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年辰妙,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汽纤。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡上岗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蕴坪,到底是詐尸還是另有隱情肴掷,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布背传,位于F島的核電站呆瞻,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏径玖。R本人自食惡果不足惜痴脾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望梳星。 院中可真熱鬧赞赖,春花似錦滚朵、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至匿垄,卻和暖如春移宅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背椿疗。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工漏峰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人届榄。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓浅乔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親痒蓬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子童擎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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