将“重新加载”信号从一个视图发送到另一个视图的最佳实践

时间:2023-01-15 20:45:58

My goal is to notify a UITableView to refresh itself every time some configurations have changed. The problem is that the configuration view is "not" on the same view that produces the signal. (Yes, I used Tabbed Application.)

我的目标是每次更改某些配置时通知UITableView刷新自己。问题是配置视图在产生信号的同一视图上“不”。 (是的,我使用了Tabbed应用程序。)

Currently I use a sort of global variable in AppDelegate for detecting the change in one view, and do the check in another view. This is fine but the code is not readable as it is so tightly coupling. Is there an elegant method for doing this? Do I miss something in this programming framework?

目前,我在AppDelegate中使用一种全局变量来检测一个视图中的更改,并在另一个视图中执行检查。这很好,但代码不可读,因为它是如此紧密耦合。这样做有一种优雅的方法吗?我在这个编程框架中是否遗漏了什么?

If there were such an elegant way, I suppose the refreshing process of UITableView should happen as soon as the notification occurs. In this case, I would like to know whether it's possible to delay UITableView from refreshing itself until viewDidAppear occurs.

如果有这么优雅的方式,我想UITableView的刷新过程应该在通知发生时立即发生。在这种情况下,我想知道是否有可能延迟UITableView刷新自己,直到发生viewDidAppear。

4 个解决方案

#1


2  

I would use KVO (Key Value Observing) to keep track of when it changes:

我会使用KVO(键值观察)来跟踪它何时发生变化:

 - (void)viewDidLoad {
      [super viewDidLoad];  

      // Note that you can use the options to get the new value passed when it
      // changes if you want to update immediately.
      [configurationObject addObserver:self forKeyPath:@"configurationItem" options:0 context:nil];
 }

 - (void)viewDidUnload {
      [super viewDidUnload];
      [configurationObject removeObserver:self forKeyPath:@"configurationItem"];
 }

 // Note that I would refresh in viewWillAppear instead of viewDidAppear
 - (void)viewWillAppear:(BOOL)animated {
      [super viewWillAppear:animated];
      if (self.needToRefreshData == YES) {
           [self.tableView refreshData];
      }
 }

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
      if (keyPath isEqualToString:@"configurationItem") {
           [self.needToRefreshData = YES];
      }
 }

#2


2  

Use Delegation Design Pattern to pass data from one View Controller to the Other.

使用委托设计模式将数据从一个View Controller传递到另一个。

For example, let's say one Tab shows a list of cars in a UITableViewController and you have another view that let's a user add a new car to the list. You can let the UITableViewController

例如,假设一个选项卡显示UITableViewController中的汽车列表,并且您有另一个视图,让用户将新汽车添加到列表中。你可以让UITableViewController

  • Adopt AddCarViewController's protocol
  • 采用AddCarViewController的协议

  • Set itself as a Delegate for AddCarViewController's protocol
  • 将自己设置为AddCarViewController协议的委托

  • Implement its protocol method
  • 实现其协议方法

  • Execute the protocol method when informed
  • 通知后执行协议方法

You can then let the AddCarViewController

然后你可以让AddCarViewController

  • Create a Protocol
  • 创建协议

  • Declare object reference Delegate with getter and setter methods
  • 使用getter和setter方法声明对象引用Delegate

  • Define a method under that protocol
  • 根据该协议定义方法

  • Inform the Delegate when the Save action is performed
  • 执行保存操作时通知代理

Take a look at the following sample code for your UITableViewController

查看UITableViewController的以下示例代码

@interface ViewController : UITableViewController <AddCarViewControllerDelegate>

             :
             :

// The addCar: method is invoked when the user taps the Add button created at run time.
- (void)addCar:(id)sender
{
// Perform the segue named ShowAddCar
[self performSegueWithIdentifier:@"ShowAddCar" sender:self];
}

             :
             :

// This method is called by the system whenever you invoke the method     performSegueWithIdentifier:sender:
// You never call this method. It is invoked by the system.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSString *segueIdentifier = [segue identifier];

    if ([segueIdentifier isEqualToString:@"ShowAddCar"]) {

        // Obtain the object reference of the destination view controller
        AddCarViewController *addCarViewController = [segue destinationViewController];

        // Under the Delegation Design Pattern, set the addCarViewController's delegate to be self
        addCarViewController.delegate = self;

        // Instantiate a Save button to invoke the save: method when tapped
        UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
                                       initWithBarButtonSystemItem:UIBarButtonSystemItemSave
                                       target:addCarViewController action:@selector(save:)];

        // Set up the Save custom button on the right of the navigation bar
        addCarViewController.navigationItem.rightBarButtonItem = saveButton;

    }

}

                 :
                 :


- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave:    (BOOL)save {
                 :
                 :
}

Sample code for the AddCarViewController is here

AddCarViewController的示例代码在这里

@protocol AddCarViewControllerDelegate;

@interface AddCarViewController : UIViewController

@property (nonatomic, strong) IBOutlet UITextField *carMake;
@property (nonatomic, strong) IBOutlet UITextField *CarName;
@property (nonatomic, assign) id <AddCarViewControllerDelegate> delegate;

// The keyboardDone: method is invoked when the user taps Done on the keyboard
- (IBAction)keyboardDone:(id)sender;

// The save: method is invoked when the user taps the Save button created at run time.
- (void)save:(id)sender;

@end

/*
 The Protocol must be specified after the Interface specification is ended.
 Guidelines:
 - Create a protocol name as ClassNameDelegate as we did above.
 - Create a protocol method name starting with the name of the class defining the protocol.
 - Make the first method parameter to be the object reference of the caller as we did below.
 */
@protocol AddCarViewControllerDelegate
- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave:(BOOL)save;
@end

#3


1  

Well, one approach would be to have some common class (singleton perhaps which app delegate kind of is) that keeps track of your model, when the settings viewController detects a change it can mark the model as changed, then when the view in question comes in to view, ie, viewDidAppear gets called, it can query the model to see if the changed flag has been set, if it has then you know to reload the table view, otherwise you dont...

好吧,一种方法是使用一些公共类(单例或类似于app委托类型)来跟踪你的模型,当设置viewController检测到变化时它可以将模型标记为已更改,然后当有问题的视图出现时在查看中,即viewDidAppear被调用,它可以查询模型以查看是否已设置更改的标志,如果已经知道重新加载表视图,否则你不...

Another way could be to use notification center for it, if your view is loaded it can sign up for the notifications of the model change, in which at point it sets a flag that it needs to reload the table view next time it comes on screen..

另一种方法是使用通知中心,如果您的视图已加载,它可以注册模型更改的通知,在此点它设置一个标志,它需要在下次屏幕上重新加载表视图..

hope this helps

希望这可以帮助

#4


1  

You could store the configuration in core data and use an NSFetchedResultsController with the dependant view controller set as a delegate. This way your view controller will get a callback whenever the data is changed.

您可以将配置存储在核心数据中,并使用NSFetchedResultsController并将从属视图控制器设置为委托。这样,只要数据发生更改,视图控制器就会收到回调。

Apple has some boilerplate code to handle the updates as well

Apple也有一些样板代码来处理更新

#1


2  

I would use KVO (Key Value Observing) to keep track of when it changes:

我会使用KVO(键值观察)来跟踪它何时发生变化:

 - (void)viewDidLoad {
      [super viewDidLoad];  

      // Note that you can use the options to get the new value passed when it
      // changes if you want to update immediately.
      [configurationObject addObserver:self forKeyPath:@"configurationItem" options:0 context:nil];
 }

 - (void)viewDidUnload {
      [super viewDidUnload];
      [configurationObject removeObserver:self forKeyPath:@"configurationItem"];
 }

 // Note that I would refresh in viewWillAppear instead of viewDidAppear
 - (void)viewWillAppear:(BOOL)animated {
      [super viewWillAppear:animated];
      if (self.needToRefreshData == YES) {
           [self.tableView refreshData];
      }
 }

 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
      if (keyPath isEqualToString:@"configurationItem") {
           [self.needToRefreshData = YES];
      }
 }

#2


2  

Use Delegation Design Pattern to pass data from one View Controller to the Other.

使用委托设计模式将数据从一个View Controller传递到另一个。

For example, let's say one Tab shows a list of cars in a UITableViewController and you have another view that let's a user add a new car to the list. You can let the UITableViewController

例如,假设一个选项卡显示UITableViewController中的汽车列表,并且您有另一个视图,让用户将新汽车添加到列表中。你可以让UITableViewController

  • Adopt AddCarViewController's protocol
  • 采用AddCarViewController的协议

  • Set itself as a Delegate for AddCarViewController's protocol
  • 将自己设置为AddCarViewController协议的委托

  • Implement its protocol method
  • 实现其协议方法

  • Execute the protocol method when informed
  • 通知后执行协议方法

You can then let the AddCarViewController

然后你可以让AddCarViewController

  • Create a Protocol
  • 创建协议

  • Declare object reference Delegate with getter and setter methods
  • 使用getter和setter方法声明对象引用Delegate

  • Define a method under that protocol
  • 根据该协议定义方法

  • Inform the Delegate when the Save action is performed
  • 执行保存操作时通知代理

Take a look at the following sample code for your UITableViewController

查看UITableViewController的以下示例代码

@interface ViewController : UITableViewController <AddCarViewControllerDelegate>

             :
             :

// The addCar: method is invoked when the user taps the Add button created at run time.
- (void)addCar:(id)sender
{
// Perform the segue named ShowAddCar
[self performSegueWithIdentifier:@"ShowAddCar" sender:self];
}

             :
             :

// This method is called by the system whenever you invoke the method     performSegueWithIdentifier:sender:
// You never call this method. It is invoked by the system.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSString *segueIdentifier = [segue identifier];

    if ([segueIdentifier isEqualToString:@"ShowAddCar"]) {

        // Obtain the object reference of the destination view controller
        AddCarViewController *addCarViewController = [segue destinationViewController];

        // Under the Delegation Design Pattern, set the addCarViewController's delegate to be self
        addCarViewController.delegate = self;

        // Instantiate a Save button to invoke the save: method when tapped
        UIBarButtonItem *saveButton = [[UIBarButtonItem alloc]
                                       initWithBarButtonSystemItem:UIBarButtonSystemItemSave
                                       target:addCarViewController action:@selector(save:)];

        // Set up the Save custom button on the right of the navigation bar
        addCarViewController.navigationItem.rightBarButtonItem = saveButton;

    }

}

                 :
                 :


- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave:    (BOOL)save {
                 :
                 :
}

Sample code for the AddCarViewController is here

AddCarViewController的示例代码在这里

@protocol AddCarViewControllerDelegate;

@interface AddCarViewController : UIViewController

@property (nonatomic, strong) IBOutlet UITextField *carMake;
@property (nonatomic, strong) IBOutlet UITextField *CarName;
@property (nonatomic, assign) id <AddCarViewControllerDelegate> delegate;

// The keyboardDone: method is invoked when the user taps Done on the keyboard
- (IBAction)keyboardDone:(id)sender;

// The save: method is invoked when the user taps the Save button created at run time.
- (void)save:(id)sender;

@end

/*
 The Protocol must be specified after the Interface specification is ended.
 Guidelines:
 - Create a protocol name as ClassNameDelegate as we did above.
 - Create a protocol method name starting with the name of the class defining the protocol.
 - Make the first method parameter to be the object reference of the caller as we did below.
 */
@protocol AddCarViewControllerDelegate
- (void)addCarViewController:(AddCarViewController *)controller didFinishWithSave:(BOOL)save;
@end

#3


1  

Well, one approach would be to have some common class (singleton perhaps which app delegate kind of is) that keeps track of your model, when the settings viewController detects a change it can mark the model as changed, then when the view in question comes in to view, ie, viewDidAppear gets called, it can query the model to see if the changed flag has been set, if it has then you know to reload the table view, otherwise you dont...

好吧,一种方法是使用一些公共类(单例或类似于app委托类型)来跟踪你的模型,当设置viewController检测到变化时它可以将模型标记为已更改,然后当有问题的视图出现时在查看中,即viewDidAppear被调用,它可以查询模型以查看是否已设置更改的标志,如果已经知道重新加载表视图,否则你不...

Another way could be to use notification center for it, if your view is loaded it can sign up for the notifications of the model change, in which at point it sets a flag that it needs to reload the table view next time it comes on screen..

另一种方法是使用通知中心,如果您的视图已加载,它可以注册模型更改的通知,在此点它设置一个标志,它需要在下次屏幕上重新加载表视图..

hope this helps

希望这可以帮助

#4


1  

You could store the configuration in core data and use an NSFetchedResultsController with the dependant view controller set as a delegate. This way your view controller will get a callback whenever the data is changed.

您可以将配置存储在核心数据中,并使用NSFetchedResultsController并将从属视图控制器设置为委托。这样,只要数据发生更改,视图控制器就会收到回调。

Apple has some boilerplate code to handle the updates as well

Apple也有一些样板代码来处理更新