# 如何调试

由于目前Apple的安全限制,暂无法使用相关调试工具进行断点调试,现阶段您可以使用如alertshowHUD等方法在屏幕上输出您需要的调试信息。

# QuartzCore

IOS设备给用户的视觉反馈都是基于QuartzCore框架来进行的,利用该框架可以实现一些美观的动画效果,如果您的插件中需要进行UI渲染等,可以自行学习QuartzCore框架

# UIKit

以下内容摘自Apple开发者文档 (opens new window),如果您有兴趣可以前往阅读

UIKit 框架提供了 iOS 或 Apple tvOS app 所需的基础架构。它提供了用于实施界面的窗口和视图架构,用于向 app 提供多点触控和其他类型输入的事件处理基础架构,以及管理用户、系统和 app 之间互动所需的主运行循环。该框架提供的其他功能包括动画支持、文档支持、绘图和打印支持、当前设备的相关信息、文本管理和显示、搜索支持、辅助功能支持、app 扩展支持和资源管理。

# 申请官方签名

# 申请渠道

请按照规范格式,发送邮件至 service@marginnote.com ,技术人员会在72小时内完成审核并予以答复。

邮件标题:插件签名申请-插件名-论坛ID

范例:插件签名申请-MDXdict-Petter1925

邮件正文: 1,插件功能描述 2,技术说明

邮件附件:以未签名格式打包的mnaddon文件,用于功能测试

请注意,不规范的邮件格式可能无法被客服系统正确识别。

# 插件签名的权限和功能

# 即时向用户推送更新

未经认证的插件更新需要用户自行下载覆盖安装新版本,不利于及时修复bug。官方认证后,可通过MN服务器在线向终端用户推送插件升级服务,方便用户及时升级到最新版本。

说明:后续更新需要重新按上述流程申请签名,但申请同时我们可以提供代为推送更新的服务。(基于新旧签名识别已安装用户,icloud Kit推送)

# 免去额外设置

未经认证的插件需要用户额外设置开启“允许加载未经认证的插件”,官方认证后可免除设置并直接安装。

# 关闭弹窗警告

未经认证的插件会在用户使用MN时会弹出警示窗口,官方认证后,使用插件则不会弹窗。

# 其他

# 常见问题

# AutoTitle 剖析

您或许已经了解了上述的API内容,但对于如何组织一个专属于您的插件还感到些许迷茫,我们为您提供的AutoTitle插件中的main.js的详细注释版本,需要您结合之前所掌握的内容,来认真阅读与思考,相信您很快就可以上手制作属于自己的插件了。

当然,纸上谈兵是远远不够的,我们非常希望您可以即刻行动,将您的想法付诸实践,这样您才能正常理解本文提到的所有概念。当然,若您发现文本存在错误,或您有更好的想法,欢迎与我们进行交流!

JSB.newAddon = function(mainPath){//这是JSBridge的方法,您不需要关心这里
    var newAddonClass = JSB.defineClass('AutoTitle : JSExtension', /*Instance members*/{
      //Window initialize
      sceneWillConnect: function() {//这里便是之前提到的生命周期函数了,您在sceneWillConnect下所编写的代码,会在窗口激活时被执行
          self.webController = WebViewController.new();//这里创建了一个WebViewController的实例
      },
      //Window disconnect
      sceneDidDisconnect: function() {
      },
      //Window resign active
      sceneWillResignActive: function() {
      },
      //Window become active
      sceneDidBecomeActive: function() {
      },
      notebookWillOpen: function(notebookid) {//这里是notebookWillOpen生命周期,会在笔记本被打开时执行其中的代码
        //NSNotificationCenter是关于消息通知的一个类
        //defaultCenter()是NSNotificationCenter中的类方法,它可以返回该类的实例
        //addObserverSelectorName()是关于添加观察者的方法,显然该方法需要创建实例后调用

        NSNotificationCenter.defaultCenter().addObserverSelectorName(self,'onProcessExcerptText:','ProcessNewExcerpt');
        //当ProcessNewExcerpt发生时,即有新摘录时,触发onProcessExcerptText事件,该事件在后续需要您详细定义其操作内容
        NSNotificationCenter.defaultCenter().addObserverSelectorName(self,'onProcessExcerptText:','ChangeExcerptRange');
        //当ChangeExcerptRange发生时,即修改摘录时,触发onProcessExcerptText事件
        self.autotitle = NSUserDefaults.standardUserDefaults().objectForKey('marginnote_autotitle');
        //绑定插件
      },
      notebookWillClose: function(notebookid) {
        //这里则是当笔记本关闭时,将相关的消息通知关闭
        NSNotificationCenter.defaultCenter().removeObserverName(self,'ProcessNewExcerpt');
        NSNotificationCenter.defaultCenter().removeObserverName(self,'ChangeExcerptRange');
      },
      documentDidOpen: function(docmd5) {
      },
      documentWillClose: function(docmd5) {
      },
      controllerWillLayoutSubviews: function(controller) {
      },
      queryAddonCommandStatus: function() {
        if(Application.sharedInstance().studyController(self.window).studyMode < 3)//判断当前模式,即在只读模式下插件不生效
          return {image:'title.png',object:self,selector:'toggleAutoTitle:',checked:(self.autotitle?true:false)};
        return null;
      },
      //Clicking note
      onProcessExcerptText: function(sender){//这里便是之前触发的事件内容,其中定义了当摘录被创建或改变时需要执行的内容
        if(!Application.sharedInstance().checkNotifySenderInWindow(sender,self.window))return;//仅仅处理当前窗口的内容
        if(!self.autotitle)return;//判断插件是否生效
        var noteid = sender.userInfo.noteid;//获取笔记ID
        var note = Database.sharedInstance().getNoteById(noteid);//通过笔记ID获取笔记
        if(note && note.excerptText && note.excerptText.length > 0 && note.excerptText.length <= 250 && !note.groupNoteId){//笔记存在且摘录文本存在且摘录文本长度不为0且摘录文本长度不足250且没有成组
          var timerCount = 0;//计数器置零
          NSTimer.scheduledTimerWithTimeInterval(1,true,function(timer){
            var text = note.excerptText.split("**").join("");//将摘录文本处理后存入text变量
            if(text && text.length){
              UndoManager.sharedInstance().undoGrouping('AutoTitle',note.notebookId,function(){
                note.noteTitle = text;//将text赋值给笔记标题
                note.excerptText = '';//将笔记摘录置空
                Database.sharedInstance().setNotebookSyncDirty(note.notebookId);//同步修改至数据库
              });
              NSNotificationCenter.defaultCenter().postNotificationNameObjectUserInfo('RefreshAfterDBChange',self,{topicid:note.notebookId});
            }
            timerCount++;
            if(timerCount >= 4){
              timer.invalidate();
            }
          });
        }
      },
      toggleAutoTitle: function(sender) {//这里是插件的开关判断,如果您没有兴趣探究,可以将一下内容稍加修改,便可作为您的插件的开关选择
        var lan = NSLocale.preferredLanguages().length?NSLocale.preferredLanguages()[0].substring(0,2):'en';
        if(self.autotitle){
          self.autotitle = false;
          if(lan == 'zh')
            Application.sharedInstance().showHUD('自动设置标题已关闭',self.window,2);
          else
            Application.sharedInstance().showHUD('Auto title is turned off',self.window,2);
        }
        else{
          self.autotitle = true;
          if(lan == 'zh')
            Application.sharedInstance().showHUD('创建摘录后,摘录内容将自动被设置为笔记标题',self.window,2);
          else
            Application.sharedInstance().showHUD('After creating an excerpt, the excerpt will be automatically set as the note title',self.window,2);
        }
        NSUserDefaults.standardUserDefaults().setObjectForKey(self.autotitle,'marginnote_autotitle');
        Application.sharedInstance().studyController(self.window).refreshAddonCommands();
      },
    }, /*Class members*/{
      addonDidConnect: function() {
      },
      addonWillDisconnect: function() {
      },
      applicationWillEnterForeground: function() {
      },
      applicationDidEnterBackground: function() {
      },
      applicationDidReceiveLocalNotification: function(notify) {
      },
    });
    return newAddonClass;
  };