<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Subject on Chen</title><link>https://hechen.github.io/zh-cn/tags/subject/</link><description>Recent content in Subject on Chen</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 18 Mar 2019 17:29:54 +0800</lastBuildDate><atom:link href="https://hechen.github.io/zh-cn/tags/subject/index.xml" rel="self" type="application/rss+xml"/><item><title>关于 Mac 应用的自启动是如何做到的</title><link>https://hechen.github.io/zh-cn/post/autostartwhenlogin/</link><pubDate>Mon, 18 Mar 2019 17:29:54 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/autostartwhenlogin/</guid><description>&lt;p&gt;开机自启动是 Cocoa 应用最常见的一种功能，尤其是针对需要常驻 Menu 的服务来说更是如此，今天我们对开机启动项的功能加入做个梳理。&lt;/p&gt;
&lt;p&gt;在 &lt;a href="https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/CreatingLoginItems.html#//apple_ref/doc/uid/10000172i-SW5-SW1"&gt;Daemons and Services Programming Guide&lt;/a&gt; 中我们能找到关于自启动项的开发说明：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Applications can contain a helper application as a full application bundle, stored inside the main application bundle in the Contents/Library/LoginItems directory. Set either the LSUIElement or LSBackgroundOnly key in the Info.plist file of the helper application’s bundle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Use the SMLoginItemSetEnabled function (available in OS X v10.6.6 and later) to enable a helper application. It takes two arguments, a CFStringRef containing the bundle identifier of the helper application, and a Boolean specifying the desired state. Pass true to start the helper application immediately and indicate that it should be started every time the user logs in. Pass false to terminate the helper application and indicate that it should no longer be launched when the user logs in. This function returns true if the requested change has taken effect; otherwise, it returns false. This function can be used to manage any number of helper applications.&lt;/p&gt;</description></item><item><title>Mac 平台上那些 Dockless 的 App 都是如何实现的？</title><link>https://hechen.github.io/zh-cn/post/dockless-cocoaapps/</link><pubDate>Wed, 13 Mar 2019 17:29:54 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/dockless-cocoaapps/</guid><description>&lt;p&gt;Menu Only 算是 Cocoa App 中最常见的一项，它使得 App 不占用你的 Dock 栏，在多 workspace 的时候也不影响正常使用，随时都可以在屏幕的菜单栏中执行快捷操作。尤其是针对一些需要便捷性要求比较高的应用来讲，Menu bar 的功能必不可少。本文就简单介绍一下关于 Menu App 中关键的几个开发步骤。&lt;/p&gt;
&lt;h1 id="介绍"&gt;介绍&lt;/h1&gt;
&lt;p&gt;以 &lt;a href="https://www.shimovpn.com/"&gt;Shimo&lt;/a&gt; 这个 App 举例，&lt;/p&gt;
&lt;p&gt;&lt;img alt="Shimo" loading="lazy" src="https://i.imgur.com/vT7EEVK.png"&gt;&lt;/p&gt;
&lt;p&gt;在其设置选项卡中能看到 Show Shimo in 的选项菜单，其中有三项：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Menubar only&lt;/li&gt;
&lt;li&gt;Dock only&lt;/li&gt;
&lt;li&gt;Menubar &amp;amp; Dock&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="Shimo Menu" loading="lazy" src="https://i.imgur.com/WywIVkV.png"&gt;&lt;/p&gt;
&lt;p&gt;这也是常见的 Cocoa 应用的模式支持，很多常见的 App 都支持，比如 DayOne，Dash，Todoist 等。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Dash" loading="lazy" src="https://i.imgur.com/ioUvpNw.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Todoist" loading="lazy" src="https://i.imgur.com/ORYAEcZ.png"&gt;&lt;/p&gt;
&lt;p&gt;其实核心功能有两点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可以显示或者隐藏 Dock 图标；&lt;/li&gt;
&lt;li&gt;可以显示或者隐藏 Menu 菜单这两者的组合。&lt;/li&gt;
&lt;/ol&gt;
&lt;h1 id="核心步骤"&gt;核心步骤&lt;/h1&gt;
&lt;h2 id="dock"&gt;Dock&lt;/h2&gt;
&lt;p&gt;普通的 Cocoa Application 创建之后，默认都是 Dock 上展示的，如果想隐藏 Dock 图标，首先它需要有这个能力，这个能力是通过 info.plist 文件中的 Key 来指定的，这个 Key 就是 &lt;code&gt;LSUIElement&lt;/code&gt;，我们将其值设置为 true&lt;/p&gt;</description></item><item><title>在英文语言系统的 Safari 中使用印象笔记</title><link>https://hechen.github.io/zh-cn/post/using-yinxiangbiji-system-english/</link><pubDate>Wed, 13 Mar 2019 00:30:49 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/using-yinxiangbiji-system-english/</guid><description>&lt;blockquote&gt;
&lt;p&gt;因为 Evernote 在国内使用了不同的账户体系，去年团队也彻底独立出来，但是 Safari 的剪藏插件是同一个，&lt;a href="https://safari-extensions.apple.com/details/?id=com.evernote.safari.clipper-Q79WDW8YH9"&gt;下载地址&lt;/a&gt;，但是当你系统是英文系统的时候，使用 Safari 剪藏有点尴尬，就是根本找不到印象笔记的登录入口，该文做个记录。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;首先 Safari 默认是没有 Develop 菜单的，因此先开启 Develop 模式，进入 Safari 的选项设置面板中，最后 Advanced 选项卡最下方如图勾选。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-39-08_A" loading="lazy" src="https://i.imgur.com/J5HvXn1.png"&gt;&lt;/p&gt;
&lt;p&gt;此时，在 Safari 的顶部菜单中会多出 Develop 菜单入口，如下图所示：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-40-57_A" loading="lazy" src="https://i.imgur.com/OxPf4Ji.png"&gt;&lt;/p&gt;
&lt;p&gt;安装印象笔记的插件之后，点击 Safari 上的印象笔记剪藏按钮，你会发现直接带到 Evernote 的登录界面，没有渠道让我登录印象笔记，即使你 Safari 已经登录了印象笔记，登录的 Session 和 Cookie 信息并没有任何用。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-50-48_A" loading="lazy" src="https://i.imgur.com/gVTWCOc.png"&gt;&lt;/p&gt;
&lt;p&gt;这时，你就登录国际版 Evernote 账户，如果你没有，那就注册一个。总之，你需要先登录进去，因为我们必须让剪藏菜单展示出来才行。&lt;/p&gt;
&lt;p&gt;登录 Evernote 之后，打开一个页面进行剪裁，之后如图，我们终于看到了剪藏菜单：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-52-36_A" loading="lazy" src="https://i.imgur.com/VtvROcP.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-53-45_A" loading="lazy" src="https://i.imgur.com/LsitsxG.png"&gt;&lt;/p&gt;
&lt;p&gt;点击下方的 Options，打开设置选项&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-56-16_A" loading="lazy" src="https://i.imgur.com/KpQefZc.png"&gt;&lt;/p&gt;
&lt;p&gt;此时，因为刚才开启了 Develop 菜单选项卡，此时右键点击该窗口，&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-58-02_A" loading="lazy" src="https://i.imgur.com/ylecnyS.png"&gt;&lt;/p&gt;
&lt;p&gt;点击 Inspect Element 打开页面元素查看器，在下方元素展示区为焦点的前提下，Command +F 搜索 &lt;strong&gt;DeveloperContainer&lt;/strong&gt; 关键字。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_12-59-28_A" loading="lazy" src="https://i.imgur.com/0DSMO39.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Capto_Capture 2019-03-13_01-02-18_A" loading="lazy" src="https://i.imgur.com/t3uqMa4.png"&gt;&lt;/p&gt;</description></item><item><title>为 NSView 增加 backgroundColor</title><link>https://hechen.github.io/post/nsview-backgroundcolor/</link><pubDate>Mon, 11 Mar 2019 15:19:49 +0800</pubDate><guid>https://hechen.github.io/post/nsview-backgroundcolor/</guid><description>&lt;p&gt;NSView 作为 Cocoa 中最基本的构成元素，是构成整个 Mac App 视图体系的基础，和 UIView 在 iOS 世界中的位置一样重要，可是在 UIView 里司空见惯的背景色设置，在 NSView 中却不见身影。&lt;/p&gt;
&lt;p&gt;在 iOS 中，设置 UIView 的背景色很简单。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;UIKit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;frame&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;UIView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;而在 macOS 上，使用 NSView 背景色需要如下设置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="nc"&gt;Cocoa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;frame&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CGRect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CGSize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;view&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wantsLayer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSColor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cgColor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;UIView 自身有一个 backgroundColor 的属性，而在 NSView 中是没有的，你需要做的是设置 View 所包含的 layer 的背景色。而且 layer 是 Optional 的，也就意味着 NSView 默认是不包含 layer 的，所以你还需要设置 wantsLayer 为 true 让 NSView 创建 layer 出来。&lt;/p&gt;
&lt;p&gt;在 Apple 的&lt;a href="https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer"&gt;官方文档&lt;/a&gt; 有针对 wantsLayer 的一些说明&lt;/p&gt;</description></item><item><title>RxSwift 中的几种 Subject</title><link>https://hechen.github.io/zh-cn/post/rxswift-subjects/</link><pubDate>Tue, 26 Feb 2019 10:24:38 +0800</pubDate><guid>https://hechen.github.io/zh-cn/post/rxswift-subjects/</guid><description>&lt;blockquote&gt;
&lt;p&gt;文中所用插图均出自书籍 《RxSwift - Reactive Programming with Swift》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Subject 在 Rx 的世界里是这么一种存在，其既可以作为观测者，也可以作为被观测者。自然而然想到的是 Subject 本身就可以作为一种过渡桥接信号的手段，它订阅某个信号，一旦信号收到序列，转头它就又把信号散发给自己的观测者了。&lt;/p&gt;
&lt;p&gt;在 RxSwift 的世界里涉及几种 Subject，这里先行做个记录：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PublishSubject&lt;/li&gt;
&lt;li&gt;BehaviorSubject&lt;/li&gt;
&lt;li&gt;ReplaySubject&lt;/li&gt;
&lt;li&gt;Variable （预计在 5.0 废弃）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="publishsubject"&gt;PublishSubject&lt;/h2&gt;
&lt;p&gt;PublishSubject 只给订阅者发送新元素，也就是订阅者只能接受到订阅之后发出的信号。一图胜千言，看👇&lt;/p&gt;
&lt;p&gt;&lt;img alt="publishSubject" loading="lazy" src="https://i.imgur.com/Cw4FjCT.png"&gt;&lt;/p&gt;
&lt;p&gt;其中第一排是某个作为 Observable 角色的 Subject，自左至右为严格时间线， 2 和 3 排均为订阅了该 Subject 的观测者，向上的虚线表明订阅的时机，可以看到观测者只能接收到订阅之后由 Subject 新发射出的信号。&lt;/p&gt;
&lt;h2 id="behaviorsubject"&gt;BehaviorSubject&lt;/h2&gt;
&lt;p&gt;BehaviorSubject 会在订阅者订阅之后发送最新的一个信号元素，自然需要你在初始化该对象的时候给其设定初始化值（否则对于第一次订阅的观测者来说，哪来的最新发射的元素呢？）。如果初始化的时候无法提供默认值，那可能你就需要用到上面的 PublishSubject 了。其中常用的 Variable 和 BehaviorRelay 都是对其的封装。&lt;/p&gt;
&lt;p&gt;&lt;img alt="BehaviorSubject" loading="lazy" src="https://i.imgur.com/ei6pCwT.png"&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看到和上方 Publish 区别的是其在观测者订阅之后会收到最新的最后一个信号。&lt;/p&gt;
&lt;h2 id="replaysubject"&gt;ReplaySubject&lt;/h2&gt;
&lt;p&gt;顾名思义，ReplaySubject 会重新发射在订阅者订阅之间发出的信号，具体发多少是由 bufferSize 决定的，其指定需要 cache 多少个信号，如下图中所示，Buffer Size 为 2，因此当第二个订阅者订阅 Subject 之后，其会收到最新的 1 和 2 两个信号。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-swift" data-lang="swift"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;/// 指定发射多少最新信号&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ReplaySubject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;.&lt;/span&gt;&lt;span class="n"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bufferSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="replaySubject" loading="lazy" src="https://i.imgur.com/uuHAQ9c.png"&gt;&lt;/p&gt;</description></item></channel></rss>