将数据从WatchKit中的模式视图传回

当以模态方式呈现或推送接口控制器时,我们可以指定context参数以将一些数据传递到新控制器,如下所示。

 // Push [self pushControllerWithName:@"MyController" context:[NSDictionary dictionaryWithObjectsAndKeys:someObject, @"someKey", ..., nil]]; // Modal [self presentControllerWithName:@"MyController" context:[NSDictionary dictionaryWithObjectsAndKeys:someObject, @"someKey", ..., nil]]; 

我的问题是,我们怎么能做到相反?

假设我们为用户提供了一个模式控制器,用于从列表中select一个项目,然后返回到主控制器,我们如何获取已被选中的项目?

我写了一个在WatchKit中使用Delegation的完整示例,在上下文中传递委托实例,并从模态中调用委托函数: 以下是GitHub上的完整项目示例

这里是这个例子的主要类:

InterfaceController.swift

这是主控制器,他的视图上有一个标签和一个button。 当你按下button时,presentItemChooser被调用,并呈现ModalView(ModalInterfaceController)。 我将上下文中的InterfaceController实例传递给模态。 重要的是这个控制器实现`ModalItemChooserDelegate'function(协议定义在模态文件中)

 class InterfaceController: WKInterfaceController, ModalItemChooserDelegate { @IBOutlet weak var itemSelected: WKInterfaceLabel! var item = "No Item" override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. } override func willActivate() { // This method is called when watch view controller is about to be visible to user itemSelected.setText(item) super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } func didSelectItem(itemSelected: String) { self.item = itemSelected } @IBAction func presentItemChooser() { self.presentControllerWithName("ModalInterfaceController", context: self) } } 

ModalInterfaceController.swift

这是我的模态控制器的类。 我举行我以前的控制器的参考( self.delegate = context as? InterfaceController )。 当选中一行时,我会在解除它之前调用我的委托函数didSelectItem(selectedItem)

 protocol ModalItemChooserDelegate { func didSelectItem(itemSelected:String) } class ModalInterfaceController: WKInterfaceController { let rowId = "CustomTableRowController" let items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"] var delegate: InterfaceController? @IBOutlet weak var customTable: WKInterfaceTable! override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) self.delegate = context as? InterfaceController // Configure interface objects here. println(delegate) loadTableData() } override func willActivate() { // This method is called when watch view controller is about to be visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer visible super.didDeactivate() } private func loadTableData(){ customTable.setNumberOfRows(items.count, withRowType: rowId) for(i, itemName) in enumerate(items){ let row = customTable.rowControllerAtIndex(i) as! TableRowController row.fillRow(itemName) } } override func table(table: WKInterfaceTable, didSelectRowAtIndex rowIndex: Int) { let selectedItem = items[rowIndex] self.delegate?.didSelectItem(selectedItem) self.dismissController() } } 

这就是我如何将数据传回给我以前的控制器。 如果是更好的方法让我知道,我会接受。 🙂

您可以通过在上下文中传递self来通过协议传回信息:

InterfaceController.m

 // don't forget to conform to the protocol! @interface InterfaceController() <PictureSelectionControllerDelegate> //... // in some method [self pushControllerWithName:@"PictureSelectionController" context:@{@"delegate" : self}]; 

并设置委托像这样:

PictureSelectionController.m

 @property (nonatomic, unsafe_unretained) id<PictureSelectionControllerDelegate> delegate; // ... - (void)awakeWithContext:(id)context { [super awakeWithContext:context]; // Configure interface objects here. if ([context isKindOfClass:[NSDictionary class]]) { self.delegate = [context objectForKey:@"delegate"]; } } 

不要忘记宣布你的协议:

PictureSelectionController.h

 @protocol PictureSelectionControllerDelegate <NSObject> - (void)selectedPicture:(UIImage *)picture; @end 

然后你可以从PictureSelectionController.m调用该方法:

 - (IBAction)buttonTapped { // get image UIImage *someCrazyKatPicture = //... [self.delegate seletedPicture:someCrazyKatPicture]; } 

并在InterfaceController.m中的委托方法中接收它:

 - (void)selectedPicture:(UIImage *)picture { NSLog(@"Got me a cat picture! %@", picture); } 

正如ghr所说,这需要更多的解释。 简单的(如果hacky)的方式是使呈现控制器成为您传递到呈现的控制器的上下文的一部分。 那样的话,你可以在需要的时候回到展示控制器。 一种方法是使用一个NSDictionary作为你的上下文,并存储一个带有对呈现控制器的引用的特殊键。 希望这可以帮助。

我一直在testing传递self到控制器(模式或不),并使用didDeactivate作为调用委托方法的一种方式,但问题是,它被称为每当屏幕被解雇,或者当一个新的视图呈现。 我刚刚开始使用WatchKit,所以我可能在这里完全错误。

我的代表

 @class Item; @class ItemController; @protocol AddItemDelegate <NSObject> - (void)didAddItem:(ItemController *)controller withItem:(Item *)item; 

我的根控制器

 @interface ListController() <AddItemDelegate> ... - (void)table:(WKInterfaceTable *)table didSelectRowAtIndex:(NSInteger)rowIndex { // TODO: How do we pass data back? Delegates? Something else? if ([self.items[rowIndex] isEqualToString:@"Item 1"]) { // TODO: Do I really want to pass along a single object here? [self pushControllerWithName:@"Item" context:self]; } } ... #pragma mark - AddItemDelegate - (void)didAddItem:(ItemController *)controller withItem:(Item *)item { NSLog(@"didAddItem:withItem: delegate called."); } 

我的孩子控制器

 @property (nonatomic, strong) Item *item; @property (nonatomic, weak) id<AddItemDelegate> delegate; ... - (void)awakeWithContext:(id)context { [super awakeWithContext:context]; // TODO: Check that this conforms to the protocol first. self.delegate = context; } ... - (void)didDeactivate { [super didDeactivate]; [self.delegate didAddItem:self withItem:self.item]; } 

使用block和segue从watchOS接口控制器传回数据

在interfaceController之间来回传递数据并不那么简单。 在WatchKit中有一个segue进程,但是第一个问题是没有prepareForSegue,你无法到达segue的destinationViewController,所以你不能很容易地将东西注入新的控制器(WatchOS 3 – 4)。 在向后的方向没有出口,所以你不能达到放松继续。

另一个问题是,这些解决scheme尝试更新willActivate方法中的第一个interfaceController的数据和用户界面,该方法在手表屏幕清醒时会被触发 – 所以非常频繁 – 这可能会导致问题并且复杂化。

正如上面的答案所描述的那样,编程实践主要是使用委托并使用segue的上下文注入自己

但是使用委托有点复杂,所以我使用更现代的块,我认为更好,更优雅。

让我们看看如何:

首先让我们在Apple Watch的故事板的Interface Builder中准备segue,只需要按下一个button,然后按下Ctrlbutton并命名segue即可。

用于Apple Watch界面的InterfaceBuilder

然后在源接口控制器的.h文件中,我们将其命名为SourceInterfaceController.h声明块的属性:

 @property (nonatomic, strong) BOOL (^initNewSessionBlock)(NSDictionary *realTimeDict, NSError *error); 

然后使用contextForSegueWithIdentifier:如果有更多的segse,则使用segueIdentifier将块或任何其他数据传输到目标interfaceController。

这个Apple方法实际上使用(id)上下文作为可以是任何对象的返回对象,并且当interfaceController启动时,目标interfaceController的awakeWithContext:(id)上下文方法将使用它。

所以让我们在SourceInterfaceController.m中声明这个块,然后把它传递给上下文:

 - (id)contextForSegueWithIdentifier:(NSString *)segueIdentifier { __unsafe_unretained typeof(self) weakSelf = self; if ([segueIdentifier isEqualToString:@"MySegue"]) { self.initNewSessionBlock = ^BOOL (NSDictionary *mySegueDict, NSError *error) { [weakSelf initNewSession]; NSLog(@"message from destination IC: %@", realTimeDict[@"messageBack"]); return YES; }; return self.initNewSessionBlock; } else if ([segueIdentifier isEqualToString:@"MyOtherSegue"]) { self.otherBlock = ^BOOL (NSString *myText, NSError *error) { //Do what you like return YES; }; return self.otherBlock; } else { return nil; } } 

如果你想传输更多的数据,而不仅仅是上下文的数据块到目的接口控制器,只需要把它们包装在一个NSDictionary中。

在目标接口控制器名称中, DestinationInterfaceController.h让我们声明另一个属性来存储使用任何名称的块,但是相同的variables声明

 @property (copy) BOOL (^initNewSessionBlock)(NSDictionary *realTimeDict, NSError *error); 

然后从DestinationInterfaceController.m中的上下文获取块:

 - (void)awakeWithContext:(id)context { [super awakeWithContext:context]; self.initNewSessionBlock = context; } 

稍后在DestinationInterfaceController.m中只需触发块,例如在一个带有button的操作方法中:

 - (IBAction)initNewSessionAction:(id)sender { NSError *error = nil; NSDictionary *realTimeDict = @{@"messageBack" : @"Greetings from the destination interfaceController"}; BOOL success = self.initNewSessionBlock(realTimeDict, error); if (success) { [self popController]; } } 

该块将使用目标interfaceController范围内的数据执行source interfaceController的任何方法,因此您可以将数据发送回目标sourceController。 如果一切正常,可以使用popControllerpopupinterfaceController,并且块返回yes作为BOOL。

注意:当然,你可以使用任何types的segue,无论是push还是modal ,你也可以使用pushControllerWithName:context:来触发segue,你可以用同样的方法使用这个方法的上下文。

也许有一些其他的方法,但我更喜欢使用pushControllerWithName:方法。

根控制器:

 - (IBAction)GoToChildControllerButton { [self pushControllerWithName:@"TableInterfaceController" context:@"pass some data to child controller here..."]; } 

儿童控制器:

 - (IBAction)BackToRootControllerButton { [self pushControllerWithName:@"TableInterfaceController" context:@"pass some data back to root controller here..."]; }