Events, Protocols and Delegates 事件、协议和委托
This article presents the key iOS technologies used to receive callbacks and to populate user interface controls with data. These technologies are events, protocols, and delegates. This article explains what each of these is, and how each is used from C#. It demonstrates how Xamarin.iOS uses iOS controls to expose familiar .NET events,as well as how Xamarin.iOS provides support for Objective-C concepts such as protocols and delegates (Objective-C delegates should not be confused with C# delegates). This article also provides examples that show how protocols are used – both as the basis for Objective-C delegates and in non-delegate scenarios.
本文展示了IOS的关键技术,用来接收用户回调,以组合UI和数据。这些技术是事件,协议和代理。本文介绍了它们是什么,它们每一个在c#中如何使用。它解释了xamarin.ios如何使用ios控件并暴露出熟悉的.net事件,也说明了xamarin.ios如何提供objective-c概念,如protocol和delegate(objective-c与c#的delegate概念不同)。本文还提供了如何使用协议的例子--既有基于delegate的,也有非delegate场景的。
Overview 概览
Xamarin.iOS uses controls to expose events for most user interactions. Xamarin.iOS applications consume these events in much the same way as do traditional .NET applications. For example, the Xamarin.iOS UIButton class has an event called TouchUpInside and consumes this event just as if this class and event were in a .NET app.
xamarin.ios使用控件为大多数用户交互暴露事件。xamarin.ios应用处理事件的方式与.net 应用的方式多数相同。例如,xamarin.ios UIButton类具有一个事件TouchUpInside,其处理方式与.NET app相同。
Besides this .NET approach, Xamarin.iOS exposes another model that can be used for more complex interaction and data binding. This methodology uses what Apple calls delegates and protocols. Delegates are similar in concept to delegates in C#, but instead of defining and calling a single method, a delegate in Objective-C is an entire class that conforms to a protocol. A protocol is similar to an interface in C#, except that its methods can be optional. So for example, in order to populate a UITableView with data, you would create a delegate class that implements the methods defined in the UITableViewDataSource protocol that the UITableView would call to populate itself.
处理.NET方法,xamarin.ios暴露了另一个模型,可用于更复杂的交互与数据绑定。这一套方法在apple称为委托和协议。委托与c#中的委托概念相似,但是相比于定义和调用一个单独的方法,一个objective -c中的委托是一个遵守某个协议的完整的类。协议类似于c#的接口概念,除了它的方法可以是可选的。所以例如,为了将tableview与数据集成,你需要创建一个委托类来实现UITableViewDataSource 协议中的方法,uitableview类将会调用这些方法来填充自己。
In this article you’ll learn about all these topics, giving you a solid foundation for handling callback scenarios in Xamarin.iOS, including:
在本文中你将学习到所有这些主题,给你一个在如何处理xamarin.ios回调场景的坚实基础,包括:
- Events – Using .NET events with UIKit controls.事件-对UIKit控件使用.NET事件
- Protocols – Learning what protocols are and how they are used, and creating an example that provides data for a map annotation.协议-协议是什么及如何使用,并创建一个示例,它为地图标注提供数据
- Delegates – Learning about Objective-C delegates by extending the map example to handle user interaction that includes an annotation, then learning the difference between strong and weak delegates and when to use each of these.委托-学习objective-c委托,通过地图示例来处理包含标注的用户交互,然后学习强委托和弱委托的差异,并学习在何时使用它们。
To illustrate protocols and delegates, we’ll build a simple map application that adds an annotation to a map as shown here:
为解释协议和委托,我们将要建立一个实例地图应用,它可添加标注到地图上,如下图所示:
Before tackling this app, let’s get started by looking at .NET events under the UIKit.
开始之前,让我们先看看UIKit中如何使用.NET事件。
.NET Events with UIKit
Xamarin.iOS exposes .NET events on UIKit controls. For example, UIButton has a TouchUpInside event, which you handle as you normally would in .NET, as shown in the following code that uses a C# lambda expression:
xamarin.ios为UIKit控件暴露了.NET事件。例如UIButton具有一个TouchUpInside事件,你的处理方式与.NET方式基本相同,如下使用lambda表达式的例子:
aButton.TouchUpInside += (o,s) => { Console.WriteLine("button touched"); };
You could also implement this with a C# 2.0-style anonymous method like this one:
你也可以使用c# 2.0风格的匿名方法,如下:
aButton.TouchUpInside += delegate { Console.WriteLine ("button touched"); };
The preceding code is wired up in the ViewDidLoad method of the UIViewContoller. The aButton variable references a button, which you could add either in the iOS Designer or with code. The following figure shows this button as it is added in iOS Designer, taken from the sample that accompanies this article:
前面的代码被放到UIViewController的ViewDidLoad方法中。aButton变量引用了一个按钮,它是通过IOS 设计器或代码方式添加的。下图展示了当按钮添加到设计器上的样子:
Xamarin.iOS also supports the target-action style of connecting your code to an interaction that occurs with a control. To create a target-action for the Hello button, double click it in the iOS Designer. The UIViewController's code-behind file will be displayed and the developer will be asked to select a location to insert the connecting method:
xamarin.ios也支持目标-动作的样式来连接你的代码和发生在控件的交互。为了给hello按钮创建target-action,在ios设计器中双击它。UIViewController的后台代码文件会显示,并询问选择一个位置来插入代码:
After a location is selected, a new method is created and wired-up to the control. In the following example, a message will be written to the console when the button is clicked:
选择位置之后,一个新方法被创建处理并连接上控件。在下面的例子中,当按钮被单击,一个消息会写到控制台。
For more details about the iOS target-action pattern, see the Target-Action section of " Core Application Competencies for iOS” in Apple’s iOS Developer Library.
更多关于ios 目标-动作模式的细节,请参考:
For more information about how to use the iOS Designer with Xamarin.iOS, see the " iOS Designer Overview” documentation.
ios设计器的用法,请参考:
Events 事件
If you want to intercept events from UIControl, you have a range of options: from using the C# lambdas and delegate functions to using the low-level Objective-C APIs.
如果你想拦截UIControl的事件,你有几个选择:从使用C#lambad和delegate方法到使用低级的objective-c API。
The following section shows how you would capture the TouchDown event on a button, depending on how much control you need.
下面的小节展示了你可以捕捉按钮的TouchDown事件,依赖于你的控制需要。
C# Style
Using the delegate syntax:
UIButton button = MakeTheButton (); button.TouchDown += delegate { Console.WriteLine ("Touched"); };
If you like lambdas instead:
1 2 3 button.TouchTown += () => { Console.WriteLine ("Touched"); };
If you want to have multiple buttons use the same handler to share the same code:
void handler (object sender, EventArgs args) { if (sender == button1) Console.WriteLine ("button1"); else Console.WriteLine ("some other button"); } button1.TouchDown += handler; button2.TouchDown += handler;
Monitoring more than one kind of Event 监视超过一种的事件
The C# events for UIControlEvent flags have a one-to-one mapping to individual flags. When you want to have the same piece of code handle two or more events, use the UIControl.AddTarget
method:
C#事件,对于UIControlEvent标志与独立标志之间具有一对一的映射。当你想用一段代码来处理两个或多个事件,使用UIControl.AddTarget方法:
button.AddTarget (handler, UIControlEvent.TouchDown | UIControlEvent.TouchCancel);
Using the lambda syntax:
button.AddTarget ((sender, event)=> Console.WriteLine ("An event happened"), UIControlEvent.TouchDown | UIControlEvent.TouchCancel);
If you need to use low-level features of Objective-C, like hooking up to a particular object instance and invoking a particular selector:
如果你使用低级的Objective-c方式,就像对一个特别的对象实例使用钩子,并调用一个特别的选择器:
[Export ("MySelector")] void MyObjectiveCHandler () { Console.WriteLine ("Hello!"); } // In some other place: button.AddTarget (this, new Selector ("MySelector"), UIControlEvent.TouchDown);
Please note, if you implement the instance method in an inherited base class, it must be a public method.
请注意,如果你在被继承的基类中实现这个实例方法,请使用public方法。
Protocols 协议
A protocol is an Objective-C language feature that provides a list of method declarations. It serves a similar purpose to an interface in C#, the main difference being that a protocol can have optional methods. Optional methods are not called if the class that adopts a protocol does not implement them. Also, a single class in Objective-C can implement multiple protocols, just as a C# class can implement multiple interfaces.
协议是objective-c语言的特性,它提供了一系列的方法声明。使用它的目的与c#中接口概念有些相似,主要差别在于协议中有可选方法。如果一个类继承了协议但是没有实现可选方法,则可选方法不会被调用。并且,一个类可实现多个协议,就像c#类可实现多个接口一样。
Apple uses protocols throughout iOS to define contracts for classes to adopt, while abstracting away the implementing class from the caller, thus operating just like a C# interface. Protocols are used both in non-delegate scenarios (such as with the MKAnnotation
example shown next), and with delegates (as presented later in this document, in the Delegates section).
apple在ios中大量使用协议来定义类需要遵从的契约,当从调用者那把实现类抽离出来,因此操作很像c#接口。协议既可在非委托场景,也可在委托场景中使用。
Protocols with Xamarin.ios
Let’s take a look at an example using an Objective-C protocol from Xamarin.iOS. For this example, we’ll use the MKAnnotation
protocol, which is part of the MapKit
framework. MKAnnotation
is a protocol that allows any object that adopts it to provide information about an annotation that can be added to a map. For example, an object implementing MKAnnotation
provides the location of the annotation and the title associated with it.
让我们看一个使用objective-c协议的例子。此例中,我们将使用MKAnnotation协议,它是MapKit框架的一部分。MKAnnotation是一个协议,它允许任何对象继承它来提供信息,关于一个能被标注到地图的信息。例如,一个对象实现MKAnnotation提供一个标注的位置和标题。
In this way, the MKAnnotation
protocol is used to provide pertinent data that accompanies an annotation. The actual view for the annotation itself is built from the data in the object that adopts the MKAnnotation
protocol. For example, the text for the callout that appears when the user taps on the annotation (as shown in the screenshot below) comes from the Title
property in the class that implements the protocol:
这样,MKAnnotation协议被用来提供恰当的数据来伴随一个标注。标注的实际视图自身使用继承了MKAnnotation协议的对象。例如,当用户轻拍标注时显示的文本,来自那个类的Title属性,它实现了此协议:
As described in the next section, Protocols Deep Dive, Xamarin.iOS binds protocols to abstract classes. For the MKAnnotation
protocol, the bound C# class is named MKAnnotation
in order to mimic the name of the protocol, and it is a subclass of NSObject
, the root base class for CocoaTouch. The protocol requires a getter and setter to be implemented for the coordinate; however, a title and subtitle are optional. Therefore, in the MKAnnotation
class, the Coordinate
property is abstract, requiring it to be implemented and the Title
and Subtitle
properties are marked virtual, making them optional, as shown below:
如下节描述,深入协议,xamarin.ios将协议绑定到抽象类上。例如,对于MKAnnotation协议,绑定的c#类是MKAnnotation以匹配这个协议名称,并且它是NSObject类的子类,后者是CocoaTouch的根基类。协议要求一个getter和setter方法被实现。然而,标题和子标题是可选的。因此,在MKAnnotation类中,Coordinate属性是抽象的,要求被实现,同时Title和Subtitle属性是标记为虚拟的,使它们成为可选的,如下所示:
[Register ("MKAnnotation"), Model ] public abstract class MKAnnotation : NSObject { public abstract CLLocationCoordinate2D Coordinate { [Export ("coordinate")] get; [Export ("setCoordinate:")] set; } public virtual string Title { [Export ("title")] get { throw new ModelNotImplementedException (); } } public virtual string Subtitle { [Export ("subtitle")] get { throw new ModelNotImplementedException (); } } ... }
Any class can provide annotation data by simply deriving from MKAnnotation
, as long as at least theCoordinate
property is implemented. For example, here’s a sample class that takes the coordinate in the constructor and returns a string for the title:
任何类都可以提供annotation数据,通过简单的继承自MKAnnotation,只要实现了Coordinate属性。例如,下面是一个例子:
/// <summary> /// Annotation class that subclasses MKAnnotation abstract class /// MKAnnotation is bound by Xamarin.iOS to the MKAnnotation protocol /// </summary> public class SampleMapAnnotation : MKAnnotation { string _title; public SampleMapAnnotation (CLLocationCoordinate2D coordinate) { Coordinate = coordinate; _title = "Sample"; } public override CLLocationCoordinate2D Coordinate { get; set; } public override string Title { get { return _title; } } }
Through the protocol it is bound to, any class that subclasses MKAnnotation
can provide relevant data that will be used by the map when it creates the annotation’s view. To add an annotation to a map, simply call the AddAnnotation
method of an MKMapView
instance, as shown in the following code:
通过绑定的协议,任何MKAnnotation的子类都可以提供map创建annotation view时需要的数据。要添加标注到地图,简单调用MKMapView实例的AddAnnotation方法即可,如下面的代码:
//an arbitrary coordinate used for demonstration here var sampleCoordinate = new CLLocationCoordinate2D (42.3467512, -71.0969456); //create an annotation and add it to the map map.AddAnnotation (new SampleMapAnnotation (sampleCoordinate));
The map variable here is an instance of an MKMapView
, which is the class that represents the map itself. TheMKMapView
will use the Coordinate
data derived from the SampleMapAnnotation
instance to position the annotation view on the map.
这里的地图变量是一个MKMapView的实例,它代表了地图。MKMapView将会使用来自SampleMapAnnotation实例的坐标数据,来在地图上定位标注视图。
The MKAnnotation
protocol provides a known set of capabilities across any objects that implement it, without the consumer (the map in this case) needing to know about implementation details. This streamlines adding a variety of possible annotations to a map.
MKAnnotation协议已经提供了一组需要的能力,很多对象都可以实现它,消费者(这个例子中就是map)无需知道其实现细节。这样就可以平滑的增加许多可能的标注到地图。
Protocols Deep Dive 深入协议
Since C# interfaces don’t support optional methods, Xamarin.iOS maps protocols to abstract classes. Therefore, adopting a protocol in Objective-C is accomplished in Xamarin.iOS by deriving from the abstract class that is bound to the protocol and implementing the required methods. These methods will be exposed as abstract methods in the class. Optional methods from the protocol will be bound to virtual methods of the C# class.
由于c#接口不支持可选方法,xamarin.ios将协议映射到了抽象类。因此,实现一个objective-c的协议,在c#中的做法就是从一个抽象类继承,这个抽象类绑定到此协议并实现了必须的方法。这些方法在抽象类中被暴露为抽象方法。协议中可选的方法被绑定到c#类的虚拟方法。
For example, here is a portion of the UITableViewDataSource
protocol as bound in Xamarin.iOS:
例如,下面是xamarin.ios实现的UITableViewDataSource协议的一部分:
public abstract class UITableViewDataSource : NSObject { [Export ("tableView:cellForRowAtIndexPath:")] public abstract UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath); [Export ("numberOfSectionsInTableView:")] public virtual int NumberOfSections (UITableView tableView){...} ... }
Note that the class is abstract. Xamarin.iOS makes the class abstract to support optional/required methods in protocols. However, unlike Objective-C protocols, (or C# interfaces), C# classes don’t support multiple inheritance. This affects the design of C# code that uses protocols, and typically leads to nested classes. More about this issue is covered later in this document, in the Delegates section.
注意这个类是抽象的。xamarin.ios通过将类定义为抽象的,来支持协议中可选/必须的方法。然而,不像objective-c协议(或c#接口),c#类不支持多继承。这影响了使用协议的c#代码设计,典型的导致嵌套类。更多关于这个问题的描述在本文后面委托部分介绍。
GetCell(…)
is an abstract method, bound to the Objective-C selector,tableView:cellForRowAtIndexPath:
, which is a required method of the UITableViewDataSource
protocol. Selector is the Objective-C term for method name. In order to enforce the method as required, Xamarin.iOS declares it as abstract. The other method, NumberOfSections(…)
, is bound tonumberOfSectionsInTableview:
. This method is optional in the protocol, so Xamarin.iOS declares it as virtual, making it optional to override in C#.
GetCell是一个抽象方法,绑定到objective-c选择器,tableView:cellForRowAtIndexPath,这是一个UITableViewDataSource协议中必须的方法。选择器是一个objective-c术语名称。为了强制这个是必须的,xamarin.ios声明它为抽象的。其它方法,NumberOfSections等,绑定到numberOfSectionsInTableview。这个方法是可选的,因此xamarin.ios声明它为虚拟的,使它可以选择性的覆盖。
Xamarin.iOS takes care of all iOS binding for you. However, if you ever need to bind a protocol from Objective-C manually, you can do so by decorating a class with the ExportAttribute
. This is the same method used by Xamarin.iOS itself.
xamarin.ios承担所有ios绑定工作,然而,如果你需要手工绑定objective-c协议,你可以用ExportAttribute属性来修饰这个类。这是命名的方法,有xamarin.ios自己使用。
For more information about how to bind Objective-C types in Xamarin.iOS, see the article Binding Objective-C Types.
关于如何在xamarin.ios中绑定objective-c类型,参考:
We’re not through with protocols yet, though. They’re also used in iOS as the basis for Objective-C delegates, which is the topic of the next section.
Delegates 委托
iOS uses Objective-C delegates to implement the delegation pattern, in which one object passes work off to another. The object doing the work is the delegate of the first object. An object tells its delegate to do work by sending it messages after certain things happen. Sending a message like this in Objective-C is functionally equivalent to calling a method in C#. A delegate implements methods in response to these calls, and so provides functionality to the application.
ios使用objective-c委托来实现委托模式,通过它一个对象将工作交给另一个对象。做具体工作的对象是第一个对象的委托。当某些事发生的时候,对象通过发送消息告诉委托去做一些工作。这里的发送消息,在objective-c中是函数,在c#中是方法。委托实现方法以响应这些调用,从而为应用程序提供功能。
Delegates enable you to extend the behavior of classes without needing to create subclasses. Applications in iOS often use delegates when one class calls back to another after an important action occurs. For example, the MKMapView
class calls back to its delegate when the user taps an annotation on a map, giving the author of the delegate class the opportunity to respond within the application. You can work through an example of this type of delegate usage later in this article, in Example Using a Delegate with Xamarin.iOS.
委托使你能扩展类的行为,而无需创建子类。ios app常常使用委托,当一个类回调另一个类,在一些重要操作发生后。例如,MKMapView类回调它的委托,当用户轻拍一个地图上的标注,使得委托的作者有机会做出响应。你可以在下面看到这样的例子。
At this point, you may be wondering how a class determines what methods to call on its delegate. This is another place where you use protocols. Usually, the methods available for a delegate come from the protocols they adopt.
现在,你可能迷惑于类如何决定在它的委托中,应该调用哪个方法。这是你使用协议的另一个方面。通常,委托可用的方法来自它们采用的协议。
How Protocols are used with Delegates 协议是如何通过委托使用的
We saw earlier how protocols are used to support adding annotations to a map. Protocols are also used to provide a known set of methods for classes to call after certain events occur, such as after the user taps an annotation on a map or selects a cell in a table. The classes that implement these methods are known as the delegates of the classes that call them.
我们早先看见,协议是如何被用来支持添加标注到地图的。协议也用于为类提供一组方法来调用,当特定事件发生,例如当用户在地图上轻拍一个标注,或选择表格中的一个单元。实现这些方法的类被称作委托--调用它们的类的委托。
Classes that support delegation do so by exposing a Delegate property, to which a class implementing the delegate is assigned. The methods you implement for the delegate will depend upon the protocol that the particular delegate adopts. For the UITableView
method, you implement the UITableViewDelegate
protocol, for the UIAccelerometer
method, you would implement UIAccelerometerDelegate
, and so on for any other classes throughout iOS for which you would want to expose a delegate.
支持委托的类暴露一个Delegate属性,一个实现了委托的类被赋予。为委托实现的方法依赖于特定的委托。对于UITableView方法,你实现UITableViewDelegate协议,对于UIAccelerometer方法,你应该实现UIAccelerometerDelegate等等。
The MKMapView
class we saw in our earlier example also has a property called Delegate, which it will call after various events occur. The Delegate for MKMapView
is of type MKMapViewDelegate
. You’ll use this shortly in an example to respond to the annotation after it is selected, but first let’s discuss the difference between strong and weak delegates.
我们看到在早先的例子中,MKMapView类也有一个称为Delegate的属性,它会在后续各种事件发生时被调用。MKMapView的委托是一个MKMapViewDelegate类型。你将直接使用它来响应标注被选中,但是我们先来讨论一下强委托和弱委托。
Strong Delegates vs. Weak Delegates
The delegates we’ve looked at so far are strong delegates, meaning they are strongly typed. The Xamarin.iOS bindings ship with a strongly typed class for every delegate protocol in iOS. However, iOS also has the concept of a weak delegate. Instead of subclassing a class bound to the Objective-C protocol for a particular delegate, iOS also lets you choose to bind the protocol methods yourself in any class you like that derives from NSObject, decorating your methods with the ExportAttribute, and then supplying the appropriate selectors. When you take this approach, you assign an instance of your class to the WeakDelegate property instead of to the Delegate property. A weak delegate offers you the flexibility to take your delegate class down a different inheritance hierarchy. Let’s look at a Xamarin.iOS example that uses both strong and weak delegates.
上面我们看到的是强委托,意思是它们是强类型的。xamarin.ios为每个ios中的委托配备了一个强类型类。然而,ios也有弱委托的概念。不同于继承绑定到某个objective-c协议的类,ios也让你可以选择绑定这些协议方法到你喜欢的任何继承自NSObject的类中,使用Export属性修饰你的方法,然后提供合适的选择器。当你采用这个方法,你赋予你的类具有WeakDelegate属性而不是Delegate属性。弱委托给予你简化类层次的灵活性。让我们看看例子:
Example Using a Delegate with Xamarin.iOS
In order to execute code in response to the user tapping the annotation in our example, we can subclassMKMapViewDelegate
and assign an instance to the MKMapView
’s Delegate
property. TheMKMapViewDelegate
protocol contains only optional methods. Therefore, all the methods are virtual that are bound to this protocol in the Xamarin.iOS MKMapViewDelegate
class. When the user selects an annotation, the MKMapView
instance will send the mapView:didSelectAnnotationView:
message to its delegate. To handle this in Xamarin.iOS, we need to override the DidSelectAnnotationView (MKMapView mapView, MKAnnotationView annotationView)
method in the MKMapViewDelegate subclass like this:
在我们的例子中,为了在用户tap标注时执行代码,我们可以编写MKMapViewDelegate的子类,并将其实例赋值给MKMapView的Delegate属性。MKMapViewDelegate协议仅包含可选方法。因此,所有的方法都是虚拟的,并且绑定到Xamarin.ios MKMapviewDelegate类的协议。当用户选择一个标注,MKMapView实例将发生mapView:didSelectAnnotationView消息给其delegate。为了处理这个,我们需要在MKMapViewDelegate子类中覆盖DidSelectAnnotationView方法,如下所示:
public class SampleMapDelegate : MKMapViewDelegate { public override void DidSelectAnnotationView ( MKMapView mapView, MKAnnotationView annotationView) { var sampleAnnotation = annotationView.Annotation as SampleMapAnnotation; if (sampleAnnotation != null) { //demo accessing the coordinate of the selected annotation to //zoom in on it mapView.Region = MKCoordinateRegion.FromDistance( sampleAnnotation.Coordinate, 500, 500); //demo accessing the title of the selected annotation Console.WriteLine ("{0} was tapped", sampleAnnotation.Title); } } }
The SampleMapDelegate class shown above is implemented as a nested class in the controller that contains the MKMapView
instance. In Objective-C, you’ll often see the controller adopt multiple protocols directly within the class. However, since protocols are bound to classes in Xamarin.iOS, the classes that implement strongly typed delegates are usually included as nested classes.
SampleMapDelegate类作为一个嵌套类实现,在包含MKMapView实例的控制器中。在objective-c中,你会经常看到控制器直接在类中遵从多个协议 。然而,在xamarin.ios中由于协议绑定到类,实现强类型delegate的类常常包含嵌套类。
With the delegate class implementation in place, you only need to instantiate an instance of the delegate in the controller and assign it to the MKMapView
’s Delegate
property as shown here:
delegate类的实现就绪后,你唯一要做的就是在控制器中实例化delegate并将它赋给MKMapView的delegate属性,如下所示:
public partial class Protocols_Delegates_EventsViewController : UIViewController { SampleMapDelegate _mapDelegate; ... public override void ViewDidLoad () { base.ViewDidLoad (); //set the map's delegate _mapDelegate = new SampleMapDelegate (); map.Delegate = _mapDelegate; ... } class SampleMapDelegate : MKMapViewDelegate { ... } }
In order to use a weak delegate to accomplish the same thing, you need to bind the method yourself in any class that derives from NSObject
and assign it to the WeakDelegate
property of the MKMapView
. Since the UIViewController
class ultimately derives from NSObject
(like every Objective-C class in CocoaTouch), we can simply implement a method bound to mapView:didSelectAnnotationView:
directly in the controller and assign the controller to MKMapView
’s WeakDelegate
, avoiding the need for the extra nested class. The code below demonstrates this approach:
为了使用弱delegate来完成同样的事,你需要自己绑定方法,在任何继承NSObject的对象中,并将其赋值给MKMapView的WeakDelegate属性。由于UIViewController类完全继承自NSObject,我们可以简单实现一个方法绑定到 mapView:didSelectAnnotationView,直接在控制器中,并将控制器赋值给MKMapView的WeakDelegate属性,避免了使用额外的嵌套类。下面的代码演示了这种方法:
public partial class Protocols_Delegates_EventsViewController : UIViewController { ... public override void ViewDidLoad () { base.ViewDidLoad (); //assign the controller directly to the weak delegate map.WeakDelegate = this; } //bind to the Objective-C selector mapView:didSelectAnnotationView: [Export("mapView:didSelectAnnotationView:")] public void DidSelectAnnotationView (MKMapView mapView, MKAnnotationView annotationView) { ... } }
When running this code, the application behaves exactly as it did when running the strongly typed delegate version. The benefit from this code is that the weak delegate doesn’t require the creation of the extra class that was created when we used the strongly typed delegate. However, this comes at the expense of type safety. If you were to make a mistake in the selector that was passed to the ExportAttribute
, you wouldn’t find out until runtime.
当运行这个代码,应用程序的行为与使用强类型delegate版本的完全一样。这个代码的好处是不必创建额外的嵌套类。然而,这带来了类型安全的隐患。如果你不小心将弄错了传给ExportAttribute的selector,你只有在运行时才能发现这个问题。
Events and Delegates 事件和委托
Delegates are used for callbacks in iOS similarly to the way .NET uses events. To make iOS APIs and the way they use Objective-C delegates seem more like .NET, Xamarin.iOS exposes .NET events in many places where delegates are used in iOS.
ios中的委托与.NET中的事件相似,都是用于回调。为了让ios api和objective-c委托的使用方式看起来更像.NET,xamarin.ios在ios使用委托的地方都暴露了.NET事件。
For example, the earlier implementation where the MKMapViewDelegate
responded to a selected annotation could also be implemented in Xamarin.iOS by using a .NET event. In that case, the event would be defined in MKMapView
and called DidSelectAnnotationView
. It would have an EventArgs
subclass of typeMKMapViewAnnotationEventsArgs
. The View
property of MKMapViewAnnotationEventsArgs
would give you a reference to the annotation view, from which you could proceed with the same implementation you had earlier, as illustrated here:
例如,前面 MKMapViewDelegate响应annotation被选择的委托,在xamarin.ios中也可以使用.NET 事件。在此例中,事件将会在MKMapView中定义并且调用
DidSelectAnnotationView。它将会有个
EventArgs的子类型MKMapViewAnnotationEventsArgs。MKMapViewAnnotationEventsArgs的view属性将给你一个标注视图的引用,从这个引用,你可以做与之前相同的事情,代码如下:
map.DidSelectAnnotationView += (s,e) => { var sampleAnnotation = e.View.Annotation as SampleMapAnnotation; if (sampleAnnotation != null) { //demo accessing the coordinate of the selected annotation to //zoom in on it mapView.Region = MKCoordinateRegion.FromDistance ( sampleAnnotation.Coordinate, 500, 500); //demo accessing the title of the selected annotation Console.WriteLine ("{0} was tapped", sampleAnnotation.Title); } };
Summary
This article covered how to use events, protocols, and delegates in Xamarin.iOS. We saw how Xamarin.iOS exposes normal .NET style events for controls. Next we learned about Objective-C protocols, including how they are different from C# interfaces and how Xamarin.iOS uses them. Finally, we examined Objective-C delegates from a Xamarin.iOS perspective. We saw how Xamarin.iOS supports both strongly and weakly typed delegates, and how to bind .NET events to delegate methods.
本文覆盖了在xamarin.ios中如何使用事件、协议、委托。我们看到xamarin.ios如何为控件暴露了.NET风格的事件。接下来我们学习了objective-c协议概念,包括它们与c#接口概念的异同,以及如何使用它们。最后,我们检验了objective-c委托,用xamarin.ios方法。我们看到xamarin.ios如何既支持强类型,也支持弱类型委托,并且如何绑定.net事件到委托方法。
Interacting with controls in Xamarin.iOS
- PDF for Offline Use:
- Sample Code:
- Related Articles:
- Related SDKs:
原文:http://developer.xamarin.com/guides/ios/application_fundamentals/delegates,_protocols,_and_events/