Sources 開發(fā)日記五(代碼展示頁面)

轉(zhuǎn)自我自己的 blog:Sources 開發(fā)日記五(代碼展示頁面)

好久沒寫 blog 了,上一篇距離現(xiàn)在居然有一個(gè)半月了外永,而上一篇關(guān)于 Sources 的開發(fā)日記竟然相隔快兩個(gè)月了寂诱。得趕快把 v1.0 的最后兩篇寫完歉摧,然后全身心進(jìn)入下一個(gè)版本的開發(fā)和記錄屈尼。

這一篇講的部分應(yīng)該是整個(gè) App 最出彩的地方(顏色很多),展示代碼躬充,而且是帶有語法高亮的展示逃顶。

語法高亮的實(shí)現(xiàn)方案

如何給代碼在 iOS 上進(jìn)行語法著色顯示雁社,這是這個(gè) app 開發(fā)前我能想到的最大的一個(gè)難題差牛。我看過幾篇關(guān)于利用 Core Text 來給文本中不同的部分來設(shè)置不同顏色的 blog,但是前提是要知道哪些部分是哪些類型司顿。關(guān)鍵字伴找,字符串盈蛮,數(shù)字,自建類型技矮,注釋……要把這些東西從一個(gè)代碼文件中分析出來抖誉,實(shí)現(xiàn)一個(gè)語法分析器,對(duì)于現(xiàn)在的我來說簡直不可能衰倦。

于是在 Google 里搜索「code highlight」袒炉,找到了我的解決方案,https://highlightjs.org樊零。這個(gè)網(wǎng)站就是在 Web 端給各種語言(目前是166種)提供語法高亮我磁,而且包含了多種主題(目前是77種)。只需要在網(wǎng)頁中使用這個(gè)網(wǎng)站提供的 js驻襟,并指定代碼段的 class 就可以實(shí)現(xiàn)夢(mèng)想中的語法高亮夺艰!

所以在 iOS 端如何實(shí)現(xiàn)也就確定了,web view沉衣。

Syntax-highlighten Code

加載代碼

HTML Template

既然是 web view郁副,就需要跟 HTML 打交道了。根據(jù) Github API 下載得到的代碼是 plain text豌习,想要語法高亮就要按照 highlightjs 的教程加載 js 到 HTML存谎,所以要自定義一個(gè) HTML 模板用來加載 js 和 代碼。

<!DOCTYPE html>
<html lang="en">
<head>
    <title>#title#</title>
    <link rel="stylesheet" href="#theme#.css">
    <meta name='viewport' content='initial-scale=1.0; maximum-scale=2.0;'>
    <script src="highlight.pack.js"></script>
    <script>hljs.initHighlightingOnLoad();</script>

</head>
<body>
<pre><code class="hljs">

#code#

</code></pre>
</body>
</html>

以上代碼中的 #title #theme #code 都是 placeholder斑鸦,在獲取代碼后用文件名愕贡、選擇的主題和代碼文本來替換。

CodeViewController 中下載代碼之前需要獲得這個(gè)模板的字符串:

private func htmlTemplateString() -> String? {
    let path = NSBundle.mainBundle().URLForResource("template", withExtension: "html")!
    let str: String?
    do {
        str = try String(contentsOfURL: path)
    } catch {
        str = nil
    }
    return str
}

下載代碼

我用的是 WKWebView巷屿。因?yàn)?WKWebView 目前還無法在 Storyboard 中使用固以,所以只能在 code 中進(jìn)行設(shè)置。


override func viewDidLoad() {
    super.viewDidLoad()

    navigationItem.title = file.name

    let config = WKWebViewConfiguration()
    config.preferences.javaScriptEnabled = true
    webView = WKWebView(frame: view.bounds, configuration: config)
    view.insertSubview(webView, belowSubview: favoriteButton)
    self.theme = NSUserDefaults.standardUserDefaults().stringForKey("default_theme") ?? "default"
    downloadSourceCode()
}

{% endcodeblock%}

語法高亮的主題默認(rèn)是 default嘱巾,如果用戶有選擇其它主題就會(huì)存入 User Defaults 中憨琳,這樣 `CodeViewController` 在每次加載后都會(huì)設(shè)置為上一次選擇的主題。

下載代碼的邏輯是這樣的:
1. 先獲取 HTML 模板和下載API
2. 根據(jù) API 去下載代碼
3. 如果 API 對(duì)應(yīng)的文件是代碼文件(文本文件)就將代碼字符串進(jìn)行轉(zhuǎn)義旬昭、placeholder 替換篙螟,用 web view 加載
4. 如果不是代碼文件,就提示用戶问拘,并直接返回文件列表

{% codeblock Download Code lang:swift %}
private func downloadSourceCode() {
    if let template = htmlTemplateString(), downloadURLString = file.downloadURLString {

        let url = NSURL(string: downloadURLString)!

        EZLoadingActivity.show("loading source", disableUI: true)

        Alamofire.request(.GET, url)
            .responseData(completionHandler: { (response) in
                EZLoadingActivity.hide()
                self.setFavoriteButton()
                if let htmlData = response.data {
                    if let dataString = String(data: htmlData, encoding: NSUTF8StringEncoding) {
                        let escapeString = dataString.stringByReplacingOccurrencesOfString("<", withString: "<")
                            .stringByReplacingOccurrencesOfString(">", withString: ">")
                        self.contentString = escapeString
                        let htmlString = template.stringByReplacingOccurrencesOfString("#code#", withString: escapeString)
                            .stringByReplacingOccurrencesOfString("#title#", withString: self.file.name ?? "")
                            .stringByReplacingOccurrencesOfString("#theme#", withString: self.theme)
                        dispatch_async(dispatch_get_main_queue(), {
                            self.webView.loadHTMLString(htmlString, baseURL: NSBundle.mainBundle().bundleURL)
                        })
                    } else {
                        // not a text file, show alert and then pop back

                        let alertController = UIAlertController(title: "", message: "This file is not a source code file", preferredStyle: .Alert)
                        let alertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: { ( _ ) in
                            self.navigationController?.popViewControllerAnimated(true)
                        })
                        alertController.addAction(alertAction)
                        RecentsManager.sharedManager.recents.removeFirst()
                        self.presentViewController(alertController, animated: true, completion: nil)
                    }
                }
            })
    }
}

語法高亮主題列表

其它的代碼查看 App 關(guān)于語法高亮主題的選擇上都只是列出個(gè)名稱列表而已遍略,通過名稱并不能直觀的知道該主題是個(gè)什么樣子的惧所,必須設(shè)置后才能一窺究竟。
我在這部分做了一點(diǎn)微小的工作:將主題的整體配色加入到列表中绪杏。

Theme list

用戶點(diǎn)擊一個(gè) theme 后下愈,會(huì)返回到 CodeViewController 并重新加載上面的 HTML string,具體代碼就不貼了蕾久,可以到我的 github 中查看势似。
弄這個(gè) list 純是個(gè)手工活,因?yàn)檫@個(gè) list 并不是動(dòng)態(tài)加載的僧著,而是我將各個(gè)主題的配色都手工提取出來做成了一個(gè)數(shù)組:

Theme array

要問我為什么要手工搞這個(gè)數(shù)組履因,下面就是原因。

Theme CSS

highlightjs 中每一個(gè) theme 其實(shí)是一個(gè) css盹愚,就是定義了 HTML 中各個(gè) class 的顏色栅迄,以 default 為例:

...

.hljs {
  display: block;
  overflow-x: auto;
  padding: 0.5em;
  background: #F0F0F0;
}


/* Base color: saturation 0; */

.hljs,
.hljs-subst {
  color: #444;
}

.hljs-comment {
  color: #888888;
}

.hljs-keyword,
.hljs-attribute,
.hljs-selector-tag,
.hljs-meta-keyword,
.hljs-doctag,
.hljs-name {
  font-weight: bold;
}


/* User color: hue: 0 */

.hljs-type,
.hljs-string,
.hljs-number,
.hljs-selector-id,
.hljs-selector-class,
.hljs-quote,
.hljs-template-tag,
.hljs-deletion {
  color: #880000;
}

.hljs-title,
.hljs-section {
  color: #880000;
  font-weight: bold;
}

.hljs-regexp,
.hljs-symbol,
.hljs-variable,
.hljs-template-variable,
.hljs-link,
.hljs-selector-attr,
.hljs-selector-pseudo {
  color: #BC6060;
}


/* Language color: hue: 90; */

.hljs-literal {
  color: #78A960;
}

.hljs-built_in,
.hljs-bullet,
.hljs-code,
.hljs-addition {
  color: #397300;
}

...

每一個(gè) css 中的 class 都不盡一樣,不過大部分還都是定義了四五個(gè)顏色皆怕,分別對(duì)應(yīng)背景霞篡、關(guān)鍵字、自定義類型端逼、字符串朗兵、數(shù)字和注釋等等。因?yàn)?css 沒有一個(gè)統(tǒng)一的格式標(biāo)準(zhǔn)顶滩,所以想靠動(dòng)態(tài)讀取來獲得這些顏色還是要比搞個(gè)固定的數(shù)組麻煩許多余掖,畢竟這個(gè)數(shù)組也不是太大,才70多個(gè)元素礁鲁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盐欺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子仅醇,更是在濱河造成了極大的恐慌冗美,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件析二,死亡現(xiàn)場離奇詭異粉洼,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叶摄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門属韧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蛤吓,你說我怎么就攤上這事宵喂。” “怎么了会傲?”我有些...
    開封第一講書人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵锅棕,是天一觀的道長拙泽。 經(jīng)常有香客問我,道長裸燎,這世上最難降的妖魔是什么奔滑? 我笑而不...
    開封第一講書人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮顺少,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘王浴。我一直安慰自己脆炎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開白布氓辣。 她就那樣靜靜地躺著秒裕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钞啸。 梳的紋絲不亂的頭發(fā)上几蜻,一...
    開封第一講書人閱讀 51,692評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音体斩,去河邊找鬼梭稚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛絮吵,可吹牛的內(nèi)容都是我干的弧烤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼蹬敲,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼暇昂!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起伴嗡,我...
    開封第一講書人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤急波,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瘪校,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澄暮,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年阱扬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赏寇。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡价认,死狀恐怖嗅定,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情用踩,我是刑警寧澤渠退,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布忙迁,位于F島的核電站,受9級(jí)特大地震影響碎乃,放射性物質(zhì)發(fā)生泄漏姊扔。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一梅誓、第九天 我趴在偏房一處隱蔽的房頂上張望恰梢。 院中可真熱鬧,春花似錦梗掰、人聲如沸嵌言。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摧茴。三九已至,卻和暖如春埂陆,著一層夾襖步出監(jiān)牢的瞬間苛白,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來泰國打工焚虱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留购裙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓鹃栽,卻偏偏與公主長得像缓窜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谍咆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,167評(píng)論 25 707
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫禾锤、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,105評(píng)論 4 62
  • 人閑心寂寂摹察,天高云悠悠恩掷。紅塵紛擾事,皆隨煙波流供嚎。曾經(jīng)多固執(zhí)黄娘,今日覺荒謬。不如歸園田克滴,耕讀到白頭逼争。
    成都獨(dú)行俠閱讀 233評(píng)論 2 4
  • 天氣轉(zhuǎn)涼了,秋天真的來了劝赔,北京的秋天是一年當(dāng)中最好的季節(jié)誓焦,可惜的是時(shí)間比較短,只有短短的兩個(gè)月左右着帽。 在北京多年杂伟,...
    一箭閱讀 330評(píng)論 0 3
  • 夜里的樣子 好孤單 誰在拼命失眠 走路的樣子 好心酸 誰在追趕時(shí)光 如果你愛上了ta 不能陪ta流浪 那就把贊美帶給ta
    一凡SU閱讀 180評(píng)論 0 0