HokeyPokey — WWDC講師特供XCode插件高仿版的設計與實現

   在咱們使用XCode IDE作iOS技術分享的時候,常常會涉及到多個方案的運行效果比較。切換不一樣的演示方案一般有以下三種方法:1.分紅多個XCode項目;2.分紅多個版本Branch 3.手動去註釋掉非本次運行方案的代碼塊。這三種方案在靈活性和操做速度及方便性上,或多或少有些差強人意。那麼,有沒有更好的方法呢?看多了WWDC視頻的童鞋或許會注意到這個名字:Hokey Pokey, 中文是變戲法的意思,WWDC講師在切換不一樣的演示方案時,快捷鍵喚起一個浮窗,在浮窗中勾選不一樣的演示代碼塊。筆者編碼實現的正是這樣一款WWDC講師特供XCode插件的高(簡易)仿(山寨)版(貨)。git

  在線維護:https://github.com/1962449521/HokeyPokeygithub

1、HokeyPokey插件的效果演示數據庫

  HokeyPokey菜單項安裝在Xcode View -> HokeyPokey中。有[Hide/Show]、[Make]、[Resume]三個功能項,分別用於展現/銷燬HokeyPokey工做窗口、將當前操做文件編譯至hokey pokey 管理數據庫、撤銷當前文件的全部hokey pokey操做及數據庫。工做窗口右上角放有按鈕,用於切換窗口是否永遠在最前面。相同運行方案名的分散代碼塊將被歸爲一個列表行。對某個方案的選擇經過雙擊列表項來實現。三個功能項的快捷鍵、工做窗口的表頭提供了方案定製,稍後介紹。ide

 

2、使用該插件目前版本需注意的地方ui

  1. 盡量只打開一個XCode窗口、在一個XCode窗口中只打開一個Tab項。這裏存在的問題是在切換XCode窗口或Tab項時,HokeyPokey窗口沒有同步更新,雙擊其中列表項時,會將最初文件操做結果寫入到新的代碼編輯區。編碼

  2. 撤銷HokeyPokey操做需逐個文件進行(第三個功能項Resume)。當只打開一個Xcode窗口一個Tab項時,HokeyPokey窗口會根據文件的切換自動更新。操做粒度爲一個文件而不是一個工程,所以當須要撤銷全部HokeyPokey操做時需逐個切換到操做過的文件,逐個撤銷。atom

  3. 編輯文件時,需先撤銷該文件的HokeyPokey更改(部分代碼被隱藏),而後修改後,再編譯至HokeyPokey數據庫(第二個功能項Make)。url

  4. 右上角的dock/normal能夠切換HokeyPokey窗口是否永遠保持最前面spa

 

3、關於插件的安裝和自定義設置插件

  該插件運行即安裝,從github下載插件工程到本地,使用默認設置或更改設置文件後,運行XCode項目便可。自定義設置涉及的文件爲HPKConst.h,具體以下圖。能夠更識別標誌、案、快捷鍵等。該插件做爲開源歡迎及但願童鞋們更改、使用、傳播和多提意見。但本着尊重出發請不要修改插件名、做者聲明等,

#ifndef HPKConst_h
#define HPKConst_h

// ------------ hokeypokey識別的開始和結束標誌
#define HPKStartStringTag             @"//hokey"
#define HPKEndStringTag               @"//pokey"

// ------------ 表頭和窗口狀態切換的UI文案
#define HPKHeaderTitle4Show           @"SHOW"
#define HPKHeaderTitle4Identifier     @"IDENTIFIER"

#define HPKLabel4WindowDock           @"dock"
#define HPKLable4WindowNormal         @"normal"

// ------------ 快捷鍵設置
#define HPKRequireKey4HideShow        @YES
#define HPKKeyEquivalent4HideShow     @"h"
#define HPKKeyMask4HideShow           NSShiftKeyMask | NSCommandKeyMask

#define HPKRequireKey4Make            @YES
#define HPKKeyEquivalent4Make         @"w"
#define HPKKeyMask4Make               NSShiftKeyMask | NSCommandKeyMask

#define HPKRequireKey4Resume          @YES
#define HPKKeyEquivalent4Resume       @"x"
#define HPKKeyMask4Resume             NSShiftKeyMask | NSCommandKeyMask

// ------------ 如下請勿更改
#define HPKIsShownKey                 @"isShow"
#define HPKTitleKey                   @"identifier"
#define HPKPockyResultsKey            @"pockyResultsKey"
#define HPKFileURLKey                 @"fileURLKey"

#endif /* HPKConst_h */

 

4、插件的需求和功能分析

  1. 使用場景和需求分析

  在技術分享或授課時,用於Xcode項目的運行方案區分與切換。須要與文章開始時提到的三種方法做比較,達到更加易用,省時的目的。

  2. 功能分析

  首先要求用戶給出格式化的textView並觸發hokeypokey編譯。用戶使用格式化的標誌,即用識別開始標誌(如//hokey) 和識別結束標誌(//pokey)將一個TextView內容分隔成若干塊。開始標識和結束標識之間用於存放不一樣方案的特有代碼塊,其它隔斷爲共用的代碼塊。

  在這個格式前提下,插件開發的最基本功能,能夠細化至如下幾項:

  a. 提取出方案特有代碼塊。給定格式字符串,提取出方案特有代碼塊的字符串、所在初始字符串中的Range,以及各代碼塊的方案名(標識) ;

  b. 運算得出對應方案的可運行代碼。給定格式字符串和hokeypokey提取結果,給定不一樣的方案名選擇時,能運算得出對應方案的可運行代碼;

  c. 當前方案選擇可視化。給出GUI顯示,將提取結果繪製至列表控件,並在UI上給出提示各塊顯示與否的提示;

  d. 提供方案切換方法。GUI容許用戶交互,在列表項雙擊時,可以切換方案選擇狀態、XCode代碼編輯區顯示和物理文檔。

 

5、插件的設計與實現

  功能點a、b的實現經過給NSString添加Category方法,類設計以下,對應功能a、b分別提供了兩個實例方法,並設計了HPKSearchResult模型用於存放分塊結果。

@interface HPKSearchResult : NSObject

@property (nonatomic, strong) NSString *title;
@property (nonatomic, assign) NSRange  range;
@property (nonatomic, strong) NSString *string;

-(instancetype) initWithRange:(NSRange)aRange string:(NSString *)aString title:(NSString *)aTitle;

@end


@interface NSString (HPKTextGetter)

- (NSArray<HPKSearchResult *> *) HPK_textResultsWithPairOpenString:(NSString *)open
                                                       closeString:(NSString *)close;

- (NSString *) HPK_stringBySubtractSearchResults:(NSArray<HPKSearchResult *> *) searchResults;

@end

  功能c、d主要經過一個自定義窗口以及Cocoa Binding來實現,類設計以下。窗口的標題windowTitle和列表內容cotentArray須要隨着當前操做文件的變化而變化。數據源由管理類HPKPluginMain提供,這裏簡單起見,直接將tableView、windowTitle、contentArray等屬性暴露出來,由管理類HPKPluginMain直接操做。該窗口類綁定了用戶對列表的雙擊動做,從而更新HokeyPokey窗口列表及將操做提交到管理類HPKPluginMain做實質操做。

#import <Cocoa/Cocoa.h>

@class HPKPluginMain;
@interface HPKWindowController : NSWindowController

@property (weak) HPKPluginMain *pluginMain;// 插件管理對象
@property (weak) IBOutlet NSTableView *tableView;// 插件窗口主控件

// 文案內容綁定
@property (strong)  NSString *windowTitle;
@property (strong)  IBOutlet NSArrayController *contentArray;

// 用戶雙擊事件綁定
- (void) inspect:(NSArray *)selectedObjects;

@end

@interface HPKWindowController () <NSTableViewDelegate>

// 文案內容綁定
@property (strong)  NSString *headerTitle4Show;
@property (strong)  NSString *headerTitle4Identifier;

// 切換窗口顯示是否懸浮按鈕
@property (weak) IBOutlet NSButton *btnDock;

@end

   管理類的設計,首先是做爲HokeyPokey窗口的數據源爲其提供顯示數據,第二是做爲HokeyPokey窗口的委託,處理交互引發的業務邏輯。數據源方面主要用了兩個字典來實現,其key值都是物理文件的url,originalTextDic存放其在編譯HokeyPokey時的原始字符串,contentArrDic存放各文件對應的HokeyPokey提取結果。

#import <Cocoa/Cocoa.h>

@interface HPKPluginMain : NSObject

/**
 *  根據用戶對hokey pokey窗口的點擊更新XCode顯示和物理文檔
 *
 *  @param editedTitle 要操做的方案名
 *  @param isShow      該方案是否顯示
 */
- (void) refreshEditorAndFileAtTiltle:(NSString *)editedTitle shouldShow:(BOOL)shouldShow;

@end



@interface HPKPluginMain() <NSWindowDelegate>

// hokey pokey數據庫
@property (nonatomic, strong) NSMutableDictionary *originalTextDic;
@property (nonatomic, strong) NSMutableDictionary *contentArrDic;

// 代碼編輯窗口
@property (nonatomic, strong) DVTSourceTextView  *ideSourceTextView;
@property (nonatomic, strong) IDEWorkspaceWindow *ideWorkspaceWindow;

@property (nonatomic, strong) HPKWindowController *windowController;// 持有hokeypokey窗口
@property (nonatomic, strong) WHUKVOController    *documentKVO;     // facebook的KVO類

- (void) createHokeyPokeyMenu;  // 建立菜單
- (void) createHokeyPokeyWindow;// 建立hokeypokey窗口
- ( NSURL *) activeDocumentURL; // 返回當前編輯文檔的地址
- (void) refreshHokeyPokeyWindowWithURL:(NSURL *)url;// 刷新hokeypokey窗口的數據庫源

@end

 

6、注意點和未完成的工做

  不要在平時開發中打開HokeyPokey窗口,以避免誤操做。

  只在技術分享、方案演示時使用,使用前必定要備份,以避免忘記撤消,XCode關閉致使代碼的丟失。

  使用時只打開一個XCode工程,一個Tab項。

  目前的文件切換是未支持Undo Manager的,能夠用粘貼板和虛擬鍵值來實現。在關閉窗口以前,咱們極可能忘記了HokeyPokey窗口有隱藏內容,是否須要在XCode退出時自動撤銷全部HokeyPokey操做?同時打開多個XCode工程或Tab時是否須要作同步?這些問題將在之後的版本維護中逐漸解決。

相關文章
相關標籤/搜索