8.7 版本发布—WinterCG 兼容性第一部分
了解更多

扩展 iOS 类

以下示例演示如何扩展 UIViewController

js
const MyViewController = UIViewController.extend(
  {
    // Override an existing method from the base class.
    // We will obtain the method signature from the protocol.
    viewDidLoad: function () {
      // Call super using the prototype:
      UIViewController.prototype.viewDidLoad.apply(this, arguments)
      // or the super property:
      this.super.viewDidLoad()

      // Add UI to the view here...
    },
    shouldAutorotate: function () {
      return false
    },

    // You can override existing properties
    get modalInPopover() {
      return this.super.modalInPopover
    },
    set modalInPopover(x) {
      this.super.modalInPopover = x
    },

    // Additional JavaScript instance methods or properties that are not accessible from Objective-C code.
    myMethod: function () {},

    get myProperty() {
      return true
    },
    set myProperty(x) {},
  },
  {
    name: 'MyViewController',
  }
)

NativeScript 运行时添加了 .extend API 作为一种选项,它在任何平台原生类上都可用,这些类接受一个包含该类平台实现 (classMembers) 的对象,以及一个可选的第二个参数对象,该对象定义了下面解释的 nativeSignature

您还可以将 @NativeClass() 装饰器与标准类 extends 一起使用,这可能感觉更自然一些。

在创建扩展其他类的自定义平台原生类时,始终确保其名称对系统上的其他类唯一,以避免类名冲突。

ts
@NativeClass()
class JSObject extends NSObject implements NSCoding {
  public encodeWithCoder(aCoder) {
    /* ... */
  }

  public initWithCoder(aDecoder) {
    /* ... */
  }

  public 'selectorWithX:andY:'(x, y) {
    /* ... */
  }

  // An array of protocols to be implemented by the native class
  public static ObjCProtocols = [NSCoding]

  // A selector will be exposed so it can be called from native.
  public static ObjCExposedMethods = {
    'selectorWithX:andY:': {
      returns: interop.types.void,
      params: [interop.types.id, interop.types.id],
    },
  }
}

注意

不应该有 TypeScript 构造函数,因为它不会被执行。而是覆盖其中一个 init 方法。

公开方法示例

如上所示,在 NativeScript 中扩展原生类采用以下形式

const <DerivedClass> = <BaseClass>.extend(classMembers, nativeSignature);

classMembers 对象可以包含三种类型的函数

  • 基类覆盖,
  • 原生可见函数,以及
  • 纯 JavaScript 函数

纯 JavaScript 函数无法被原生库访问。如果要使函数对原生库可见并可调用,请将 nativeSignature 参数传递给 extend,并使用所需的附加元数据提供有关函数签名的必要信息。

nativeSignature 参数是可选的,并具有以下属性

  • name - 可选,包含派生类名称的字符串
  • protocols - 可选,包含已实现协议的数组
  • exposedMethods - 可选,包含函数 名称原生函数签名 对象的字典

原生函数签名 对象有两个属性

  • returns - 必需,类型 对象
  • params - 必需,类型 对象数组

通常,类型对象是 运行时类型 之一

  • 一个构造函数,用于识别 Objective-C 类
  • interop.types 对象中的基本类型
  • 在极少数情况下,它可以是使用 interop API 描述的引用类型、结构类型等。

以下示例说明如何将纯 JavaScript 函数公开给 Objective-C API

js
const MyViewController = UIViewController.extend(
  {
    viewDidLoad: function () {
      // ...
      const aboutButton = UIButton.buttonWithType(
        UIButtonType.UIButtonTypeRoundedRect
      )
      // Pass this target and the aboutTap selector for touch up callback.
      aboutButton.addTargetActionForControlEvents(
        this,
        'aboutTap',
        UIControlEvents.UIControlEventTouchUpInside
      )
      // ...
    },
    // The aboutTap is a JavaScript method that will be accessible from Objective-C.
    aboutTap: function (sender) {
      const alertWindow = new UIAlertView()
      alertWindow.title = 'About'
      alertWindow.addButtonWithTitle('OK')
      alertWindow.show()
    },
  },
  {
    name: 'MyViewController',
    exposedMethods: {
      // Declare the signature of the aboutTap. We can not infer it, since it is not inherited from base class or protocol.
      aboutTap: { returns: interop.types.void, params: [UIControl] },
    },
  }
)

覆盖初始化器

初始化器应始终返回对对象本身的引用,如果无法初始化,则应返回 null。这就是为什么在尝试使用 self 之前需要检查它是否存在的原因。

js
const MyObject = NSObject.extend({
  init: function () {
    const self = this.super.init()
    if (self) {
      // The base class initialized successfully
      console.log('Initialized')
    }
    return self
  },
})

符合 Objective-C/Swift 协议

以下示例符合 UIApplicationDelegate 协议

js
const MyAppDelegate = UIResponder.extend(
  {
    // Implement a method from UIApplicationDelegate.
    // We will obtain the method signature from the protocol.
    applicationDidFinishLaunchingWithOptions: function (
      application,
      launchOptions
    ) {
      this._window = new UIWindow(UIScreen.mainScreen.bounds)
      this._window.rootViewController = MyViewController.alloc().init()
      this._window.makeKeyAndVisible()
      return true
    },
  },
  {
    // The name for the registered Objective-C class.
    name: 'MyAppDelegate',
    // Declare that the native Objective-C class will implement the UIApplicationDelegate Objective-C protocol.
    protocols: [UIApplicationDelegate],
  }
)

让我们看看如何通过为 Tesseract-OCR-iOS API 设置一个委托来在 TypeScript 中声明委托

ts
interface G8TesseractDelegate extends NSObjectProtocol {
  preprocessedImageForTesseractSourceImage?(
    tesseract: G8Tesseract,
    sourceImage: UIImage
  ): UIImage
  progressImageRecognitionForTesseract?(tesseract: G8Tesseract): void
  shouldCancelImageRecognitionForTesseract?(tesseract: G8Tesseract): boolean
}

实现委托

ts
// native delegates often always extend NSObject
// when in doubt, extend NSObject
@NativeClass()
class G8TesseractDelegateImpl extends NSObject implements G8TesseractDelegate {
  static ObjCProtocols = [G8TesseractDelegate] // define our native protocols

  static new(): G8TesseractDelegateImpl {
    return <G8TesseractDelegateImpl>super.new() // calls new() on the NSObject
  }

  preprocessedImageForTesseractSourceImage(
    tesseract: G8Tesseract,
    sourceImage: UIImage
  ): UIImage {
    console.info('preprocessedImageForTesseractSourceImage')
    return sourceImage
  }

  progressImageRecognitionForTesseract(tesseract: G8Tesseract) {
    console.info('progressImageRecognitionForTesseract')
  }

  shouldCancelImageRecognitionForTesseract(tesseract: G8Tesseract): boolean {
    console.info('shouldCancelImageRecognitionForTesseract')
    return false
  }
}

使用符合 G8TesseractDelegate 协议的类

ts
let delegate: G8TesseractDelegateImpl

function image2text(image: UIImage): string {
  let tess: G8Tesseract = G8Tesseract.new()

  // The `tess.delegate` property is weak and won't be retained by the Objective-C runtime so you should manually keep the delegate JS object alive as long the tessaract instance is alive
  delegate = G8TesseractDelegateImpl.new()
  tess.delegate = delegate

  tess.image = image
  let results: boolean = tess.recognize()
  if (results == true) {
    return tess.recognizedText
  } else {
    return 'ERROR'
  }
}

限制

  • 您不应该扩展已扩展的类
  • 您不能覆盖静态函数或属性
  • 您不能公开静态函数或属性
上一页
Android