青芒 for Mac客户端开发笔记

关于轻芒和青芒

轻芒阅读是我天天都会打开的app,在它提供的两百多个channel中,我订阅了其中十几个channel,很是喜欢这个产品。这个产品的创始人是王俊煜,他曾经创办了一个更有名的公司——豌豆荚。关于轻芒和俊煜的更多介绍,请参照: 前沿科技的报道。虽然报道里强调了轻芒不是青芒,可是参照知了和知乎的关系,我仍是决定把个人客户端取名为青芒。并且取名青芒的一大好处是logo好设计。作完原型的次日,我就找了非著名设计师Joseph帮我设计了一枚logo,我的以为仍是很赞的。前端

非著名设计师Joseph设计的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视图(略丑)
Notification Center视图(略丑)

Notification Center视图(略丑)windows

TouchBar视图
TouchBar视图

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界面的构建一直是值得争论的话题,大致上有三种可选方式:

  1. 纯代码手写
  2. Xib文件
  3. Stroyboard

纯手写代码

纯手写代码是极客的不二选择,对于多人协做工做是很好的选择,可是缺点主要是不能直观的看到效果、编码速度很慢,例如初始化一个自定义的Button可能就须要二十行代码,很是不利于阅读。

Xib文件

Xib解决了上面的两个问题,提高了开发效率。其实Xib就是XML格式的文件,在编译过程当中,被编译成Nib文件,每一个Nib文件跟对应的ViewController关联。Xib的缺点是:代码可能回覆盖UI的设计,并且每一个视图都须要单个的Xib,视图间的跳转依然须要代码控制。

Stroyboard

为了解决了Xib的问题,Apple提供了故事板功能。StoryBoard能够当作将不少Xib集中到了一块儿,像讲述一个故事同样,清晰的看到每一个ViewController之间的跳转关系,跳转能够不用写代码了。所以我选用了StoryBoard来构建青芒。

青芒的StoryBoard文件截图
青芒的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,这个滑动事件我不处理了,请交给别人处理吧。

NSTouchBar

为了赶时髦,应用内作了TouchBar和通知中心的内容。TouchBar须要注意的是NSWindow和NSViewController之间的联动。从NSWindow到NSViewController:

let myViewcontroller =self.window?.contentViewControlleras!mainViewController
复制代码

相反的过程:

let mywindowController=NSApplication.shared().windows[0].windowControlleras?windowController
复制代码

这样就能够作到在TouchBar中按了某个按钮,在主界面里也能够看到按钮被选中的效果,知足了一致性。

Notification Center视图

通知中心作的蛮丑的,真的是为了尝试一下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万屡次阅读,最后我欣喜的发现,王俊煜也给该微博点了赞,感谢俊昱的鼓励与确定。

微博截图
微博截图

微博截图

相关文章
相关标签/搜索