This blog post will show you how to create and use iOS delegates in NativeScript.
From What is a delegate in iOS?:
A delegate is any object that should be notified when something interesting has happened. What that "something interesting" means depends on the context: for example, a table view's delegate gets notified when the user taps on a row, whereas a navigation controller's delegate gets notified when the user moves between view controllers.
Let's take a look at UIColorPickerViewController which provides a way to present color choices a user can select. When the controller detects the user color selection, it needs a way to inform your app about the color picked. The way it does that is through a delegate. Apple provides different protocols for different purposes. For the UIColorPickerViewController
, it provides the UIColorPickerViewControllerDelegate protocol.
Using this delegate, let's update the backgroundColor
of a StackLayout
in NativeScript. To receive the selected color from the picker, we conform to the UIColorPickerViewControllerDelegate protocol.
We can always approach controller/delegate setup in 2 stages.
Note: Be sure to understand the brief but important best practice usage of delegates in NativeScript: Delegates, delegates, DELEGATES!!.
Create a delegate implementation class, let's call it ColorPickerDelegateImpl
, that extends
NSObject and conforms (aka implements
) to the delegate protocol UIColorPickerViewControllerDelegate.
@NativeClass()
class ColorPickerDelegateImpl
extends NSObject
implements UIColorPickerViewControllerDelegate {
// informs NativeScript to wire up the protocol
static ObjCProtocols = [UIColorPickerViewControllerDelegate];
// best practice to have an owner weak reference
owner: WeakRef<HelloWorldModel>;
// common pattern with statically initializing an implementation
static initWithOwner(owner: WeakRef<HelloWorldModel>) {
const delegate = <ColorPickerDelegateImpl>ColorPickerDelegateImpl.new();
delegate.owner = owner;
return delegate;
}
// implement delegate methods here...
}
Understanding highlights:
@NativeClass()
. The @NativeClass()
decorator ensures compliance with the NativeScript runtime which you can learn more about here.NSObject
most commonly because it provides all the common baseline iOS behavior our implementation needs and also because our delegate is just a protocol, aka an interface
, which cannot be extended (just conformed to by an implementation).static ObjCProtocols
Array can contain any number of protocols we want our implementation to use and informs NativeScript to wire up the specified protocols on our behalf.static initWithOwner(owner: WeakRef<HelloWorldModel>)
pattern since it allows you to pass in other references if needed as additional method arguments without interfering with a platform (super) constructor up chain. Of note, the ColorPickerDelegateImpl.new()
is a convenient simple constructor that NativeScript adds to all platform classes avoiding particular init
arguments which you may want to handle later and returns an NSObject
which is why we simply cast it to our type <ColorPickerDelegateImpl>
.The owner weak reference comes into play when wanting to pass events and data back/forth between the delegate and it's owner. Let's do exactly that by implementing several methods provided by the UIColorPickerViewControllerDelegate:
import { Color } from '@nativescript/core';
@NativeClass()
class ColorPickerDelegateImpl
extends NSObject
implements UIColorPickerViewControllerDelegate
{
// ...
// all delegate methods come from Apple documentation:
// https://developer.apple.com/documentation/uikit/uicolorpickerviewcontrollerdelegate#3635512
colorPickerViewControllerDidFinish(
viewController: UIColorPickerViewController
) {
// did close/finish event
this.owner?.deref()
.changeColor(Color.fromIosColor(viewController.selectedColor));
}
colorPickerViewControllerDidSelectColorContinuously(
viewController: UIColorPickerViewController,
color: UIColor,
continuously: boolean
) {
// selecting colors event
}
}
Create the controller which will use your delegate:
const picker = UIColorPickerViewController.alloc().init();
Init the delegate implementation we created above while assigning it to an instance property as mentioned in the best practices:
this.colorDelegate = ColorPickerDelegateImpl.initWithOwner(
new WeakRef(this)
);
Set the delegate
property the controller that needs it:
picker.delegete = this.colorDelegate;
Watch the platform come to life.
Want even more neat delegate examples? Here's another implementing table view delegates from scratch to sort list views!