背景
在上篇博客中,我介紹了mouseSync這款軟件的開發(fā)初衷、使用場景和開發(fā)的過程,感謝一些朋友試用該軟件并給我留言,提出了一些新的需求和issue。同時(shí)也感謝開發(fā)者頭條的推薦至頭版的精選板塊,讓那篇開發(fā)筆記被2萬開發(fā)者閱讀過了,收獲了github的star若干、公眾號(hào)的關(guān)注若干、贊賞約50元。真的非常感謝大家的關(guān)注、star鼓勵(lì)和金錢贊賞。
我整理了一下大家的需求,大體上可歸納為3類。
- 沒有l(wèi)ogo
- 在外部設(shè)備(鼠標(biāo)/Trackpad真正屬于的那臺(tái)Mac)上容易引起誤操作
- 一個(gè)關(guān)鍵的功能,左鍵拖拽功能,沒有實(shí)現(xiàn)
在這篇博客中,我就來寫一下對上面提到的三個(gè)需求的解決方案。完成上述三個(gè)需求后的軟件在網(wǎng)盤可下載到,最新代碼依然開源在github上。
需求
沒有l(wèi)ogo
其實(shí)這件事挺尷尬的,我不是設(shè)計(jì)師,因此平時(shí)logo的主要來源是flaticon,這次沒忍心打擾正在熟睡的非著名設(shè)計(jì)師Joseph,因此還是在flaticon上找的icon,進(jìn)行了簡單的顏色變換與組合。
icon考慮到了mouseSync具有鼠標(biāo)和藍(lán)牙的特性,因此還是比較寫實(shí)的。考慮到藍(lán)牙客戶端有外部設(shè)備和數(shù)據(jù)中心(兩臺(tái)Mac)之分,因此兩個(gè)icon雖然很相似,但仍有細(xì)微的不同。不同點(diǎn)在于,數(shù)據(jù)中心的icon多了一個(gè)類似信號(hào)指示的標(biāo)志,表示它是接收藍(lán)牙信號(hào)的一端。
由于本人設(shè)計(jì)方面實(shí)在業(yè)余,還請大家輕噴。有空再找非著名設(shè)計(jì)師Joseph實(shí)現(xiàn)一枚漂亮的logo。

icons
在外部設(shè)備端容易引起誤操作
之前的demo視頻中,為了讓兩臺(tái)電腦看起來一致,我錄的是同時(shí)打開文件,同時(shí)在圖標(biāo)上右擊,在現(xiàn)實(shí)生活中,在同一時(shí)刻同時(shí)操作兩臺(tái)Mac的需求很少不存在。通常情況下,用戶還是想對某一臺(tái)Mac進(jìn)行操作的。mouseSync的定位應(yīng)該是:幫助用戶用一個(gè)鼠標(biāo)操作兩臺(tái)電腦,且給用戶決定什么時(shí)候操作哪臺(tái),省去兩個(gè)鼠標(biāo)之間換來換去的麻煩。
之前的項(xiàng)目中,我對鼠標(biāo)事件添加的均是全局監(jiān)控事件,因此只要mouseSync生命周期沒有結(jié)束,它的操作將實(shí)時(shí)被發(fā)送到另一臺(tái)mac上。我想過兩個(gè)解決方案:
- 跟其他共享鍵盤的軟件一樣,使用全局快捷鍵對藍(lán)牙通知服務(wù)進(jìn)行開啟/關(guān)閉
- 將全局監(jiān)控事件變成局部監(jiān)控事件,藍(lán)牙通知服務(wù)僅在應(yīng)用視圖為Keywindow時(shí)有效
為什么選擇Plan B
關(guān)于方案1,我覺得作為一款軟件,快捷鍵應(yīng)當(dāng)由用戶自己決定,雖然cocoapod上已經(jīng)有非常好的項(xiàng)目KeyHolder進(jìn)行支持,但是考慮到兩個(gè)因素還是決定使用Plan B。
先看看Plan B,Plan B是這樣的,首先讓當(dāng)前的window全屏,但是里面沒有內(nèi)容,因此無論在當(dāng)前試圖下如何操作,都不會(huì)有可能造成誤操作。回到Plan A,快捷鍵的錄入放到哪里呢?如果放到當(dāng)前視圖,將會(huì)帶來一定的誤操作風(fēng)險(xiǎn);如果放到MenuBar,那么用戶將不得不退出全屏模式,到菜單欄進(jìn)行設(shè)置,這樣的解決方案破壞了用戶體驗(yàn),綜合考慮,最終還是選了Plan B。
實(shí)現(xiàn)過程中遇到的問題
讓當(dāng)前窗口一打開就全屏的方法是在ViewDidLoad方法體中進(jìn)行控制的:
override func viewDidLoad() {
super.viewDidLoad()
self.view.window?.zoom(self)
}
但是我很快就發(fā)現(xiàn)了問題:盡管已經(jīng)將應(yīng)用全屏了,但是用戶依然會(huì)有一定幾率對dock欄和頂部菜單欄進(jìn)行誤操作。用戶如果在操作另一臺(tái)電腦的時(shí)候,還得注意這臺(tái)電腦上是不是誤操作了,這不符合我們軟件開發(fā)的初衷。考慮到有些用戶將dock放在底部,有些用戶將dock放在左邊,因此,我加了這樣的限制,當(dāng)用戶將鼠標(biāo)移動(dòng)到距離屏幕邊緣50px以內(nèi)的時(shí)候,程序幫助他將鼠標(biāo)移動(dòng)至屏幕中央。參照下圖,灰色地帶就可以作為防止鼠標(biāo)誤操作的緩沖區(qū)。

防止對dock和菜單欄誤操作解決方法示意圖
閱讀過上一篇博客的朋友一定注意到了,我們的程序是記錄上一個(gè)鼠標(biāo)點(diǎn)的位置,并將上一個(gè)鼠標(biāo)點(diǎn)的位置與當(dāng)前位置相減,將位置變動(dòng)作為特征值進(jìn)行發(fā)送。因此,在鼠標(biāo)被程序移動(dòng)到屏幕中央的時(shí)候,需要將上一個(gè)位置更新為屏幕中央的坐標(biāo)。
//屏幕寬高的獲取方式
let SCREEN_WIDTH = NSScreen.main()!.frame.width
let SCREEN_HEIGHT = NSScreen.main()!.frame.height
}
左鍵拖拽功能
左鍵拖一個(gè)文件或窗口到一個(gè)新位置這個(gè)功能還是經(jīng)常被使用到的,我使用的是Trackpad,通常是使用三支拖移。想要監(jiān)聽外圍設(shè)備的這個(gè)動(dòng)作并不難,但是想讓計(jì)算機(jī)模擬這個(gè)操作相對比較困難。就像雙擊不是單純的模擬單擊事件兩次一樣,這次我又踩到坑了,drag and move事件并不是簡單的mousedown-> mousemove -> mouseup這么容易的事情。
我在Stack Overflow上找到了正確的方法:有一個(gè)叫kCGEventLeftMouseDragged的事件,專門來處理這件事。
void mouse_left_drag_to(float x, float y) {
CGEventRef left_drag_event = CGEventCreateMouseEvent(NULL, kCGEventLeftMouseDragged, CGPointMake(x, y), 0);
CGEventPost(kCGHIDEventTap, left_drag_event);
CFRelease(left_drag_event);
}
和Scroll滾輪事件一樣,這里我們采用的是Quartz模擬鼠標(biāo)事件,因此依然需要C與swift進(jìn)行混編。模擬總共可分為3步:第一步是創(chuàng)建一個(gè)Quartz鼠標(biāo)事件;第二步是講這個(gè)時(shí)間放到事件流中,并指定位置;第三步是release這個(gè)事件。
模擬這個(gè)事件的時(shí)候,隨著鼠標(biāo)拖拽后的不停移動(dòng),會(huì)不停產(chǎn)生event,而且event中x和y的偏移量始終是相對于第一次鼠標(biāo)drag按下時(shí)的坐標(biāo)。因此,我們需要記錄兩件事,第一件事是第一次drag時(shí)候的坐標(biāo),第二件事是當(dāng)前是不是開啟了新一輪的drag還是仍然在上一輪的drag中。核心代碼如下:
if dragFirst == true{
dragFirst = false
mouseLocBeforeDrag = NSEvent.mouseLocation() mouseLocBeforeDrag.y = NSHeight(NSScreen.screens()![0].frame) - mouseLocBeforeDrag.y;
mouse_left_drag_to(Float(mouseLocBeforeDrag.x)+Float(dx!),Float(mouseLocBeforeDrag.y)-Float(dy!)
}else{
mouse_left_drag_to(Float(mouseLocBeforeDrag.x)+Float(dx!),Float(mouseLocBeforeDrag.y)-Float(dy!))
}
上面的代碼有兩處需要注意:
- dragFirst初始值為true,在mouseUp之后重新置為true,表示一輪的drag已經(jīng)完成了;
- y的坐標(biāo)是-,x的坐標(biāo)是+,這是由OS X的坐標(biāo)系決定的。
最后,我用兩臺(tái)電腦同時(shí)在畫板上寫了一個(gè)牛B。mouseSync的開發(fā)工作算是告一段落了,希望對大家有所幫助!

drag事件demo
Reference
- https://stackoverflow.com/questions/1817628/clicking-the-mouse-down-to-drag-objects-on-mac
- https://www.flaticon.com
結(jié)束
試用軟件之后如果您對軟件有任何的意見與建議,歡迎留言。如果您喜歡這款軟件,也歡迎您將它推薦給您的朋友們。讓我們一起將mouseSync變得更好!
我會(huì)在訂閱號(hào)里不定期分享我個(gè)人的macOS/ios開發(fā)心得和開發(fā)筆記,也會(huì)在里面發(fā)表對于蘋果產(chǎn)品/框架/趨勢的拙見,希望愛好科技產(chǎn)品或者蘋果生態(tài)圈的開發(fā)者關(guān)注。相信本公眾號(hào)一定能給您帶來收獲和啟發(fā)。
【歡迎掃碼關(guān)注微信公眾號(hào)】

掃碼關(guān)注微信公眾號(hào) 骨灰級(jí)果粉 獲得最新文章更新