輕芒閱讀是我天天都會打開的app,在它提供的兩百多個channel中,我訂閱了其中十幾個channel,很是喜歡這個產品。這個產品的創始人是王俊煜,他曾經創辦了一個更有名的公司——豌豆莢。關於輕芒和俊煜的更多介紹,請參照: 前沿科技的報道。雖然報道里強調了輕芒不是青芒,可是參照知了和知乎的關係,我仍是決定把個人客戶端取名爲青芒。並且取名青芒的一大好處是logo好設計。作完原型的次日,我就找了非著名設計師Joseph幫我設計了一枚logo,我的以爲仍是很讚的。前端
非著名設計師Joseph設計的logoios
8月18日,我在github閒逛的時候發現了輕芒團隊仍處於內測階段的API文檔,抱着試一試的態度,我給輕芒團隊發送了內測申請的郵件,兩天後獲得了對方產品經理振輝的回覆,贊成了我參加內測的申請,在這表示感謝。收到郵件以後我興奮異常,雖然以前也作了像微博、twitter、Instagram的客戶端,可是這些產品早已有比我作的更好的產品上架到Apple Store了,估計除了我之外,沒有人會用我開發的簡陋版客戶端。而輕芒則不一樣,目前還真沒有mac和windows的客戶端。git
輕芒的用戶一般對精神生活有豐富的追求,在macOS和windows中,我估計macOS用戶會很是多,所以我選擇了macOS平臺,我認爲這對我是一個機會,可能會有很多人試用這款軟件。畢竟看到別人電腦或手機上運行着你開發的軟件,仍是會給人帶來很大的成就感的。(偷笑)github
據不徹底統計,自從開源以來,已經有100多人下載了該軟件。web
知了是知乎日報的mac客戶端,青芒跟知了有着相同的需求,即展現channel list、article list、article content三個部分,因而萌生了借鑑知了的念頭,搜了一圈以後發現github上並無相似的mac軟件開源出來,因而作了決定:本身從頭開始寫,而且寫完的第一件事就是將項目開源。我相信三段式佈局的應用會適用不少場景,而且會給初學者們帶來必定的啓發。項目開源在個人github上,歡迎fork和star,特別感謝github開發者@jydemo、@immress對本項目提出的改進與code實現,效果圖以下。json
客戶端視圖(模仿的知了)swift
Notification Center視圖(略醜)windows
TouchBar視圖api
輕芒的API遵循RPC風格,形如 域名/主體.操做,主體和操做都使用小寫開頭的駝峯命名法,整體上仍是比較容易調用的。青芒目前用到了其中的category.list、category.get、article.list、article.get。經測試,發現內測版裏提供的channel還很是有限,只有11個,因此目前選擇將全部channel展現出來。瀏覽器
開發青芒過程當中,使用到了以下技術:HTTP請求、json解析、NSSplitView、WebKit、NSTableView、多線程(GCD)、imageView、自定義NSTextField、NSScrollView、自定義Window、NSTouchBar、NCWidgetProviding等技術,下面我來詳細介紹一下青芒實現的過程。
macOS/ios界面的構建一直是值得爭論的話題,大致上有三種可選方式:
純手寫代碼是極客的不二選擇,對於多人協做工做是很好的選擇,可是缺點主要是不能直觀的看到效果、編碼速度很慢,例如初始化一個自定義的Button可能就須要二十行代碼,很是不利於閱讀。
Xib解決了上面的兩個問題,提高了開發效率。其實Xib就是XML格式的文件,在編譯過程當中,被編譯成Nib文件,每一個Nib文件跟對應的ViewController關聯。Xib的缺點是:代碼可能回覆蓋UI的設計,並且每一個視圖都須要單個的Xib,視圖間的跳轉依然須要代碼控制。
爲了解決了Xib的問題,Apple提供了故事板功能。StoryBoard能夠當作將不少Xib集中到了一塊兒,像講述一個故事同樣,清晰的看到每一個ViewController之間的跳轉關係,跳轉能夠不用寫代碼了。所以我選用了StoryBoard來構建青芒。
青芒的StoryBoard文件截圖
從StroyBoard截圖能夠清晰的看到整個界面的佈局,整個界面的佈局是模仿的知乎日報Mac客戶端(知了)作的,經典的三段式佈局,NSSplitView能夠將界面分紅左右(或上下)的兩部分。考慮到主題一列比較窄,並且主題和相應主題下的文章有強關聯關係,所以借用SplitView將Overview Controller一分爲二,這樣三段式佈局就算是完成了。
先從左邊的OverView視圖提及,這個視圖裏由兩部分組成,第一個部分是一系列的主題按鈕,點擊以後,在右邊的NSTableview裏展現對應主題下的最新文章list。最新文章list點擊以後,在右邊的DetailView中用一個WebView展示文章的詳情。
黑色的部分是TouchBar的視圖,TouchBar是蘋果在MacBook Pro 2016機型上加入的一個新的交互設備,圍繞他的討論有不少,咱們有空能夠單獨聊一下,可是爲了尊重這條價值4K的bar,我仍是決定作了相應的適配,將主題經過按鈕的形式放到了Bar上,Bar上的按鈕和OverView的按鈕是須要作聯動功能的,在下文中會提到。
耐心地拖入相關控件、控制控件之間的相對位置,最初始的原型就算是完成了。固然,通過試用,仍是能夠發現優化的部分,好比Overview裏的tableview並非頂到window title的,這樣用戶拖拽那部分的時候,窗口依然能夠移動,一樣,WebView上方也是須要留白的。雖然只是個很小的細節,但真的很討好用戶。
界面完成後,就須要作功能了,可是等等,好像看起來不大對勁,爲何效果相比於知了差不少呢?下面的截圖展示了青芒的第二版,相比於初版,已是把window的標題去掉了,但仍是給人一種寨寨的、不夠簡潔的感受。TableView中選中的顏色跟總體界面很不符。
第二版的青芒原型
爲何會出現這樣的狀況呢?由於一直到如今,咱們都是採用的系統默認選項,沒有設計人員的審美在裏面。如何給用戶帶來私人訂製的感受呢?這就須要咱們覆蓋系統的默認行爲和屬性,具體來講,就是自定義子類,繼承並覆蓋父類中不符合開發者預期的部分。
從最後的結果來看,咱們須要整個軟件看起來背景是白色的,所以咱們在每個view加載的函數中,指定背景色爲白色,使用
view.wantsLayer =true
self.view.layer?.backgroundColor=NSColor.white.cgColor
複製代碼
雖然全部視圖背景色所有設爲了白色,可是關閉、最大化、最小化按鈕依然title上,而不是overview那部分,設置title爲影藏,它們又不見了,讓他們正確顯示在overview中的作法是在window加載函數中加入:
self.window?.titleVisibility = .hidden
self.window?.titlebarAppearsTransparent =true
self.window?.styleMask.insert(.fullSizeContentView)
複製代碼
關於TableView中選中狀態的背景色,可行的方法有兩種。第一種是自定義Cell覆蓋NSTableCellView,覆蓋父類中的override var backgroundStyle:NSBackgroundStyle{}屬性。第二種方法是github上的用戶@jydemofork個人項目以後給我提的issue,自定義NSTableRowView,覆蓋父類方法:
override func drawSelection(in dirtyRect:NSRect) {
super.drawSelection(in: dirtyRect)
var slectorRect =NSInsetRect(self.bounds,0,0)
NSColor(calibratedWhite:0.92, alpha:1.0).setStroke()
NSColor(calibratedWhite:0.92, alpha:1.0).setFill()
var slectorPath =NSBezierPath(roundedRect: slectorRect, xRadius:0, yRadius:0)
slectorPath.fill()
slectorPath.stroke()
}
複製代碼
而後實現tableview的代理方法,
func tableView(_tableView:NSTableView, rowViewForRow row:Int) ->NSTableRowView? {
let rowview = MyTableRowView(frame: .zero)
return rowview
}
複製代碼
兩個主視圖之間的分隔條比較粗,總讓人以爲不美,解決方法仍是自定義。覆蓋NSSplitView,覆蓋屬性
override vardividerThickness:CGFloat{
get {return0.5}
}
複製代碼
若是用戶願意分隔條仍是能夠左右動的,想禁掉左右動的功能,實現NSSplitView的一個代理方法:
override func splitView(_ofDividerAtsplitView:NSSplitView, effectiveRect proposedEffectiveRect:NSRect, forDrawnRect drawnRect:NSRect, ofDividerAt dividerIndex:Int) ->NSRect{
return NSRect.zero
}
複製代碼
通過這一章節,不難發現仍是代碼靠譜(😎)。通過上面的調整,界面看起來簡潔、清爽了很多,能夠以假亂真了。
但願這一章節讓你們明白,想要作出看起來美的東西,必定要去大膽地替代系統的默認選項。而具體的作法一般是覆蓋父類中的屬性和方法,記得要將組件和自定義的類關聯起來。
美化後的界面
自定義TableView的Cell過程當中,因爲文章的標題一般是比較長的,所以用NSTextField沒法放下,必須使用NSTextView,而NSTextView默認是能夠上下左右滑動的,因此在文章列表中上下滑動的時候,每當滑動到TextView裏,滑動事件就會白TextView捕獲,TableView中的Scroll view沒有機會捕獲了。
解決的方法和上一節同樣,經過覆蓋cell裏(請注意是Cell,不是tableview)的scroll view,重寫hitTest方法
override func hitTest(_point:NSPoint) ->NSView? {
return nil
}
複製代碼
告訴cell,這個滑動事件我不處理了,請交給別人處理吧。
爲了趕時髦,應用內作了TouchBar和通知中心的內容。TouchBar須要注意的是NSWindow和NSViewController之間的聯動。從NSWindow到NSViewController:
let myViewcontroller =self.window?.contentViewControlleras!mainViewController
複製代碼
相反的過程:
let mywindowController=NSApplication.shared().windows[0].windowControlleras?windowController
複製代碼
這樣就能夠作到在TouchBar中按了某個按鈕,在主界面裏也能夠看到按鈕被選中的效果,知足了一致性。
通知中心作的蠻醜的,真的是爲了嘗試一下TodayExtension的功能而已。模仿知了,目前功能只是展現了首頁的文章列表,點擊文章能夠用系統默認瀏覽器打開原文。其實這也就夠了,畢竟通知中心就是爲了看個大概用的,誰也不會常常點開看。通知中心須要注意的是要自定義視圖的高度,經過 self.preferredContentSize=CGSize(width:self.view.frame.size.width, height:xxx) 完成。 最後要注意的是用URLSession請求數據,能夠防止UI卡頓,UI卡頓給用戶帶來的感受很是糟糕。
func getData(with urlString:String,success:@escaping(Data?)->Void, failure: ((Error)->Void)? =nil) {
guard let url =URL(string: urlString)else{
return
}
let task =URLSession.shared.dataTask(with: url) { (data:Data?, response:URLResponse?, error:Error?)in
DispatchQueue.main.async{
if let error = error {
failure?(error)
}else{
success(data)
}
}
}
task.resume()
}
複製代碼
青芒團隊提供的API裏有web-content這一項,只要經過webview包裝而後load一下就會將文字和圖片展現出來,可是仍是有一些細節須要考慮到的。好比有些圖片是很大的,雖然限制了窗口不得小於一個值,但有些圖片仍是遠超了這個大小。web-content是沒有標題的,看起來會比較突兀,標題和標題須要你手動加上。圖片過大時,窗口是能夠左右滑動的,怎麼把他限制不能滑動。字體如何跟其餘部分視圖的字體作到沒有違和感,都是須要考慮的問題,這些用前端裏的CSS樣式能夠控制,須要開發者有必定的前端開經驗。因爲本公衆號重心在果教,所以這裏就不具體展開敘述了,感興趣的朋友能夠參考個人github。
作完青芒以後,考慮到整個項目是用最新的swift4寫成,因而我在微博上@了swiftLanguage,博主是一個不大不小的V,關注者可能是對swift感興趣的或從業人員。短短兩天,該微博得到了1萬屢次閱讀,最後我欣喜的發現,王俊煜也給該微博點了贊,感謝俊昱的鼓勵與確定。
微博截圖