天天看點

[譯]利用js建構osx應用

<a href="http://tylergaw.com/articles/building-osx-apps-with-js" target="_blank">原文位址</a>

在這個主題上,主持人講解了<code>object-c bridge</code>,這個是非常酷的東西,可以往<code>javascript</code>中導入任何底層的方法,例如,如用你想用标準的<code>os x</code>控件來建立<code>gui</code>應用的話,你将要引入<code>cocoa</code>:

我可以這麼跟你說,隻要是用<code>objective-c</code>或者<code>swift</code>建立的app,用<code>javascript</code>都可以做出來.

了解它的最好方法就是實踐,下面我們将要實作一個簡單的app(從你電腦上選取一個圖檔并顯示它),效果如圖

[譯]利用js建構osx應用

制定一個<code>app.js</code>,包括一個視窗,一個文本标簽,一個輸入框和一個按鈕,對應的<code>class</code>名稱就是<code>nswindow</code>,<code>nstextfield</code>,<code>nstextfield</code>和<code>nsbutton</code>.

單擊選擇圖檔檔案按鈕,将會顯示一個<code>nsopenpanel</code>,它将會顯示一個檔案選擇視窗,我們還需要配置這個視窗的檔案過濾屬性,隻能選擇<code>.jpg</code>,<code>.png</code>或者<code>.gif</code>

當選擇一個圖檔之後,将會在視窗中顯示出來,并且自适應圖檔大小,視窗設定了一個最小的寬高以免圖檔被裁剪

建立一個project

打開<code>apple script editor</code>應用程式,定位到<code>applications &gt; utilities</code>,<code>script editor</code>并不是最好用的編輯器,但是現在有必要用它,它提供了很多的特性用來建立<code>js osx app</code>,并且可以用來編譯和運作你的<code>js osx app</code>,它也可以添加些擴充檔案像我們<code>app</code>需要的<code>info.plist</code>檔案.我猜也許還有别的編輯器可以做同樣的事情,不過目前我還沒有找到.

建立一個新的文檔,定位到<code>file-&gt;new</code>或者使用指令<code>cmd + n</code>,首先要做的就是儲存它為<code>application</code>,定位到<code>file-&gt;save</code>或者使用指令<code>cmd + s</code>,确認儲存之前,有兩個選項需要注意下,看下圖:

[譯]利用js建構osx應用

檔案格式選擇<code>application</code>,選中<code>stay open after run handler</code>

如果沒有選中<code>stay open after run handler</code>,打開應用之後會一閃而過,然後自動關閉,這些網上都沒有什麼教程,隻是自己幾個小時摸索出來的.

現在應該是做些有意義的時候了

添加下面兩行代碼到你的編輯器中,然後運作它,定位到<code>script-&gt;run application</code>或者<code>opt + cmd + r</code>.

這時候運作應用什麼都沒有發生,唯一看到改變的就是全局菜單欄以及<code>dock</code>,因為應用名稱以及<code>file</code>,<code>edit</code>并排在菜單欄上,應用圖檔顯示在<code>dock</code>上,這些都代表着應用已經在運作.

那麼<code>hello feenan!</code>跑哪去了呢?<code>$</code>符号是什麼,是jquery?,我們先把應用程式退出了,定位到<code>file-&gt;quit</code>或者<code>cmd + q</code>,然後我們來找找<code>nslog</code>輸出的内容.

打開<code>console app</code>,定位到<code>applications &gt; utilities &gt; console</code>,任何一個應用程式都可以記錄日志到<code>console app</code>中,它跟<code>chrome</code>,<code>firefox</code>,<code>safari</code>中的控制台沒多大差別,主要的差別在于你可以使用它來調試應用程式代替<code>website</code>

在<code>console app</code>中有很多日志資訊,你可以在右上角的輸入框中輸入<code>applet</code>來過濾日志,輸入<code>applet</code>到過濾框中之後,回到<code>script editor</code>中,再次運作應用程式,使用<code>opt + cmd + r</code>指令,控制台資訊如下圖

[譯]利用js建構osx應用

你是不是看到在控制台中顯示<code>hello feenan!</code>了,如果沒有的話,退出應用程式再次運作看看,有時候我們忘記退出應用程式,代碼并沒有再次運作.

$符号是什麼呢?

<code>$</code>讓你能夠通路<code>objective-c bridge</code>.任何時候你需要通路<code>objective-c</code>其中某個類或者常量,你都可能使用<code>$.foo</code>或者<code>objc.foo</code>,後面還有講到關于使用<code>$</code>的其它一些方法.

建立一個視窗

讓我們建立一個可以顯示并且有互動的視窗,代碼看起來像下面這樣

當這些都在合适的位置之後,我們運作它通過<code>opt + cmd + r</code>,然後我們可以說,隻需要這麼點代碼就可以啟動一個<code>app</code>并且打開一個視窗,而且我們可以移動,最小化和關閉它.

[譯]利用js建構osx應用

如果你跟我一樣沒有用<code>objective-c</code>或者<code>cocoa</code>來建立一個app的話,這些可以看起來有點難以了解,對我來說,這些方法名稱的長度有點難以接受,雖然我喜歡描述性的方法名稱,但是像<code>cocoa</code>這樣的還是太極端了.

看上面的代碼其實就是<code>javascript</code>,就跟你編寫網站代碼一樣.

第一行中的<code>stylemask</code>是幹麼的呢?它提供了一些對視窗屬性設定的功能,有<code>标題</code>,<code>關閉按鈕</code>,<code>最小化按鈕</code>,這些選項都是常量,通過<code>|</code>符号來添加多個,<code>|</code>符号是<code>c</code>裡的<code>or</code>操作符,不用去了解它的原理,隻要知道它是用來并列多個選項的就足夠了.

這裡還有一些有趣的文法需要你記住,<code>$.nswindow.alloc</code>調用<code>nswindow</code>裡的<code>alloc</code>方法,但是并沒有在後面插入<code>()</code>,這種調用方式就跟<code>js</code>裡擷取屬性一樣,但是<code>js</code>裡調用方法不是這樣的,原來,在<code>osx js</code>中,假如方法沒有參數的時候,是不能在後面加入<code>()</code>的,否則它會出現運作時錯誤.是以以後當你發現有些事件并沒有像你期望中發生時就要檢查下<code>console</code>裡的輸出内容了.

下一個要關注的事情是這個超長的方法:

<code>initwithcontentrectstylemaskbackingdefer</code>

<code>initwithcontentrect:stylemask:backing:defer:</code>

上面的描述相當于在<code>objective-c</code>中建立下面的代碼

注意下上面方法簽名中的<code>:</code>,當你想把一個<code>objective-c</code>中的方法轉換成<code>js</code>的方法時,首先需要把<code>:</code>除掉然後把跟在後面的第一個字母大寫.當看到方括号<code>[]</code>裡有兩項是,代表調用一個類或者對象的方法,<code>nswindow alloc</code>表示調用<code>nswindow</code>的<code>alloc</code>方法,換成<code>js</code>的話,需要在兩者之前添加一個<code>.</code>,像<code>nswindow.alloc</code>這樣

我認為剩下的代碼足夠用來描述建立一個視窗并顯示它,我跳過了很多關于這方面的細節說明,這個需要很多時間用來閱讀相關文檔,不過你可以這些.當你做到顯示出一個視窗來已經不錯了,讓我們做更多的事情吧

添加控件

視窗裡還需要一個标簽,一個輸入框,一個按鈕,我使用<code>nstextfield</code>和<code>nsbutton</code>來建立它,輸入下面的代碼然後運作你的app

如果應用跑起來沒問題的話,将會看到下面的效果,你可以在輸入框中輸入内容,按鈕點選沒有任何效果,不過我們後面會加些東西上去

[譯]利用js建構osx應用

看到上面的代碼是不是對添加有些疑惑,到底怎麼實作的呢?<code>textfieldlabel</code>和<code>textfield</code>非常相似,它們都是<code>nstextfield</code>的執行個體,在這裡都是通過相似的方法實作的,當你看到<code>initwithframe</code>和<code>nsmakerect</code>時,它們是建立<code>ui</code>元素的好方法,<code>nsmakerect</code>就跟它的名字一樣,用來建立有一定大小的方形用給定的位置資訊<code>(x, y, width, height)</code>.這就相當于建立了<code>objective-c</code>中的結構體資訊,在<code>js</code>中我們引用它為一個對象或者<code>hash</code>,或者一個<code>dict</code>,擁有自己的鍵值對.

建立完輸入框這後,給它賦一些屬性,<code>cocoa</code>并沒有單獨建立标簽的方法,是以這裡我們利用<code>nstextfield</code>,禁用它的編輯屬性并且設定的背景樣式來模拟标簽效果.

假如是正常的輸入框的話,其實隻需要設定一行代碼就可以了

最後的事情就是通過<code>addsubview</code>把這些控件添加到我們的<code>window</code>中去,剛開始我調用<code>window.addsubview(theview)</code>,但是并沒添加起來,最後發現隻能添加其它用<code>nsview</code>建立的執行個體,我不知道為什麼會這樣,但是想要添加<code>nswindow</code>建立的執行個體的話,隻能調用<code>window.contentview.addsubview(theview)</code>.官方文檔上是這樣描述的,<code>nsview對象是視窗裡最高的通路層次</code>.

給按鈕添加代碼

當點選按鈕的時候,我想顯示一個面闆出來,上面列出本地的檔案資訊,做這些之前,讓我們先添加一個日志資訊熱熱身.

在<code>javascript</code>中添加事件一般是給對象添加監聽,但是在<code>objective-c</code>中沒有這樣的概念,在它這裡叫消息通訊,你得發送一個包含方法名的消息給目标對象,目标對象收到這個包含方法名的消息之後才能決定做什麼,也許我說的不是很準确,但是大概就是這個意思

首先我們要做的就是添加一個<code>target</code>和一個<code>action</code>,<code>target</code>是發送給<code>action</code>的一個對象,也許現在還沒什麼意義,但是後面我會增加更多的代碼,先增加下面的一部分代碼,用來更新按鈕屬性的:

<code>appdelegate</code>和<code>btnclickhandler</code>還沒存在,是以要先建立它們,在下面的代碼有提供,然後代碼中還增加了注釋用來告訴你把這些新的代碼添加到何處

運作app,然後在<code>console</code>中檢視是否有顯示<code>clicked!</code>,如果顯示了,說明代碼沒問題,否則檢查下代碼跟文中是否有差別,然後仔細看下<code>console</code>中的錯誤資訊.

subclassing(子類)

一切看起來挺不錯的,但是上面的代碼做了什麼呢?因為并沒有寫<code>superclass</code>屬性,是以預設繼承<code>nsobject</code>,它是所有<code>objective-c</code>裡的基類, 設定<code>name</code>屬性友善後面我們用<code>$</code>或者<code>objc</code>來引用它.

<code>$.appdelegate.alloc.init</code>會建立一個<code>appdelegate</code>類執行個體,需要再次注意的是,<code>alloc</code>和<code>init</code>後面并沒有插入<code>()</code>,因為我們并沒有傳任何參數.

subclass methods(子類方法)

可以通過一個字元串内容來建立一個方法,比如上面的<code>btnclickhandler</code>,然後給它提供一個對象參數,成員包括<code>types</code>和<code>implementation</code>,官方文檔上并沒有說明<code>types</code>數組應該包括什麼,但是經過我不斷嘗試感覺它的參數說明應該是這樣的:

<code>btnclickhandler</code>沒傳回任務東西是以設定<code>return type</code>為<code>void</code>,它需要一個參數,就是發送的對象,是以這裡設定參數名為<code>id</code>,它可以表示任何對象.

<code>implementation</code>是一個普通的函數,在這裡面可以寫<code>javascript</code>,同時可以通路<code>$</code>符号以及外面定義的變量.

使用protocols的問題
choosing and displaying images(選擇并顯示圖檔)

我們準備打開一個面闆,選擇一個圖檔,然後顯示它,更新<code>btnclickhandler</code>的<code>implementation</code>函數,代碼如下

首先我們建立<code>nsopenpanel</code>一個執行個體,如果你從來沒有打開過檔案或者儲存操作,那麼預設顯示的面闆是活動檔案清單.

我隻想打開圖檔檔案,是以需要過濾檔案清單,設定<code>allowedfiletypes</code>屬性,它是一個<code>nsarray</code>類型,我們建立一個<code>js</code>數組<code>allowedtypes</code>來給它指派,但是我們需要轉換成<code>nsarray</code>類型,通過<code>$(allowedtypes)</code>,這是<code>bridge</code>橋的另外一種用法,可以通過這個方法在<code>js</code>和<code>objective-c</code>之間進行類型轉換,想轉換<code>objective-c</code>類型為<code>js</code>中對應的類型的話,使用<code>$(objcthing).js</code>方法.

打開面闆通過<code>panel.runmodal</code>方法,這個代碼會馬上執行,你可以點選<code>取消</code>和<code>确定</code>,然後我們可以通過傳回值來判斷你點選是哪個,當點選确定傳回的是<code>$.nsokbutton</code>.

另一個需要注意的是<code>panel.urls</code>,通常我們通路<code>js</code>中的數組元素是通過<code>[]</code>,因為<code>urls</code>是<code>nsarray</code>類型,是以不能通過<code>[]</code>來通路,這裡提供了<code>objectatindex</code>方法來通路,它跟<code>[]</code>的效果是一樣的.

隻要我們擷取到圖檔的<code>url</code>,那麼我們就可以建立一個<code>nsimage</code>執行個體,這裡有一個專門的方法

跟建立其它<code>ui</code>元素一樣, 這裡我們建立一個<code>nsimageview</code>執行個體來顯示圖檔

我們想視窗的大小能夠自适應圖檔的大小,但是不能低于最小寬高,為了設定視窗的大小,這裡調用<code>setframedisplay</code>方法

我們通過圖像視圖來包裝圖檔,然後把它添加到<code>window</code>視圖中,因為視窗大小改變了,是以需要重新計算居中顯示.

tidbits(花絮)

迄今為止,我們已經在<code>script editor</code>中建立了一個app并用指令<code>opt + cmd + d</code>運作它,也可以像其它app一樣,輕按兩下它的應用圖示來運作.

[譯]利用js建構osx應用

你也可以修改app圖示的路徑,替換<code>/contents/resources/applet.icns</code>就行,想通路應用的資源檔案,可以右鍵app選擇<code>show package contents</code>就行.

我對它的潛能感到非常激動,下面是我已經想到的一點觀點.當<code>yosemite</code>正式釋出之後,很多人可以坐下來開發原生app了,使用最普通的程式設計語言,而且不用下載下傳或者安裝别的東西,假如你想的話連<code>xcode</code>也可以不用安裝,完全降低了進入<code>app</code>開發的門檻,這簡直太<code>瘋狂</code>了.

我知道有很多大型應用程式通過<code>腳本</code>是辦不到的,我也沒有說<code>腳本</code>是建立app的唯一方式,但是我們可以讓一些人建立一些小的app為他自己或者别人.當一個團隊整天在指令行下工作的不舒服時,我們可以為它們建立一個<code>gui</code>程式;當需要快速,可視化的建立或者修改配置檔案時,也可以為它建立一個小的app.

當然也有其它程式設計語言可以辦到,像<code>python</code>和<code>ruby</code>也可以通路到低層api,然後建立app.隻是利用<code>javascript</code>來建立app顯的更與衆不同,這簡直有點颠覆我們的思想.這感覺就像一些網站敲響了桌面上的門,<code>apple</code>讓這個門解鎖了,我完全被它吸引了.

繼續閱讀