【问题标题】:Am I properly using delegation? If not, how should I be doing this?我是否正确使用委托?如果没有,我应该怎么做?
【发布时间】:2014-01-07 02:52:03
【问题描述】:

所以我开始涉足 iOS 开发的 Objective-C 编程。我有一个正在开发的小应用程序,没什么特别的,但可以帮助我掌握一些技巧。我遇到的问题如下:

目前,我有两节课。第一个是

ViewController

第二个是我自己创造的,叫做

UserDecision

View 控制器显示屏幕上的内容,UserDecisions 当前从屏幕上按下的按钮获取信息,并在使用我的模型类时对其执行适当的逻辑。我的问题是,如果发生某些事件,我在 UserDecision 中有一个更新 UI 方法,它需要更新 ViewController 中的按钮属性(文本、可见性等)。因此,我无法使用 ViewController 的实例,因为我无法访问屏幕上的按钮。所以为此我创建了一个委托系统:

@protocol updateUIDelegate <NSObject>

-(void)hideAll;
-(void)makeBackVisible;
-(void)updateOutput:(NSString *)output;
-(void)updateChoices:(NSString *)choices;
-(void)updateTrueButton:(NSString *)trueString;
-(void)updateFalseButton:(NSString *)falseString;
-(void)removeChoiceFromArray;

@end

上面的协议是在 UserDecision.h 中定义的,然后我将我的 ViewController 分配为我的委托:

@interface ViewController : UIViewController <updateUIDelegate>;

然后我在 ViewController.m 中清除上述方法:

#pragma - updateUIDelegates -

//Called when the last screen is displayed
-(void)hideAll{
    [_trueButton setHidden:true];
    [_falseButton setHidden:true];
    [_choicesText setHidden:true];
    [_backButton setHidden:true];
    [_resetButton setHidden:false];
}

//Makes back button visible
-(void)makeBackVisible{
    [_backButton setHidden:false];
}

//Updates the text on the false button
-(void)updateFalseButton:(NSString *)falseString{
    [_falseButton setTitle:falseString forState:UIControlStateNormal];
}

//Updates the text on the true button
-(void)updateTrueButton:(NSString *)trueString{
    [_trueButton setTitle:trueString forState:UIControlStateNormal];
}

//Updates the output text box
-(void)updateOutput:(NSString *)output{
    [_outputText setText:output];
}

//Updates the choices textbox
-(void)updateChoices:(NSString *)choices{
    if(!choicesArray){
        choicesArray = [[NSMutableArray alloc] initWithCapacity:4];
    }
    //If this is the first button press, add string to array and display
    if([_choicesText.text isEqualToString:@""]){
        [choicesArray addObject:choices];
        _choicesText.text = [NSString stringWithFormat:@"%@", choices];
    }
    //Otherwise, add the new string to the array, and print the array
    //using a comma as a way to concatinate the string and get rid of
    //the ugly look of printing out an array.
    else{
        [choicesArray addObject:choices];
        [_choicesText setText:[NSString stringWithFormat:@"%@",[choicesArray componentsJoinedByString:@", "]]];
    }
}

//Removes the last choice from the array
-(void)removeChoiceFromArray{
    [choicesArray removeLastObject];
    [_choicesText setText:[NSString stringWithFormat:@"%@", [choicesArray componentsJoinedByString:@","]]];

}

这允许我在需要时通过将它们作为消息发送到我的UserDecision 类中的self.delegate 来调用这些方法。

这是我目前的设置。我的问题已经变成我想创建一个在最后弹出的模态序列视图(在用户按下按钮以调出视图之后),然后可以将其关闭。我的问题是,从我在网上所做的阅读和研究来看,这种观点只能通过授权来驳回,除非我想让事情变得令人讨厌。现在,我试图在我的类中实现这些信息,但后来我读到一个类只能是另一个类的委托。而且由于我的ViewController(这是我的主窗口)已经是我的UserDecision 类的代表,我不能让它成为我创建的新视图的代表,因此不能关闭视图。所以,我在这里寻求您的帮助。我该如何解决这个问题?

另外,关于我的更多代码,如果你想看看,这里是我的 gitHub 的链接:https://github.com/Aghassi/Xcode/tree/master/Bubble%20Tea%20Choice/Bubble%20Tea%20Choice

【问题讨论】:

    标签: ios iphone objective-c uiviewcontroller delegates


    【解决方案1】:

    我读到一个类只能是另一个类的代表。和 因为我的 ViewController(这是我的主窗口)已经是一个委托 我的 UserDecision 类,我不能让它成为我的新视图的代表 已创建

    我不相信这是真的。您可以让 ViewController 实现许多不同的协议,因此可以委托给不同的类/对象。

    例如:(UITableViewDelegate 和 UITextViewDelegate 都可以在同一个 ViewController 上为 2 个单独的对象(UITextView 和 UITableView)实现。

    至于使用委托关闭模态窗口,另一种选择是也使用blocks

    【讨论】:

    • 这是正确的,委托模式为与其关联的消息提供了单个接收者(通常在协议中定义)。许多对象可以共享一个委托,一个对象可以同时充当该模式的多个实例的委托。 UITableViewDelegateUITableViewDataSource 是另一对常见的委托模式,通常由同一个对象实现。供将来参考objc.io/issue-7/communication-patterns.html 上的流程图​​可能是协调对象之间通信的有用工具。
    • 我不确定我是否完全遵循块是什么。您是否有一个很好的教程可以推荐,它将引导我了解块如何与示例一起使用?我不知道他们将如何解决我的问题。
    【解决方案2】:

    viewController 可以自行关闭。只需将关闭按钮连接到调用类似以下内容的函数:

    [self.presentingViewController dismissViewControllerAnimated:YES];
    

    [self.navigationController popViewControllerAnimated:YES];
    

    可以使用委托模式完成解雇,但并非所有事情都需要。

    【讨论】:

      【解决方案3】:

      viewController 类可以是多个对象的委托,因此它应该能够关闭模式视图。唯一的问题是,如果它是同一类的多个对象的委托,您可能需要检查哪个对象正在调用它。

      以tableView的委托方法为例,tableView调用它们将自己作为第一个参数传递。

      要关闭自定义模式视图,无论如何您都需要定义不同的协议,这样调用相同的方法就没有问题。

      请看下面的例子:

      @protocol OSImageViewControllerProtocol <NSObject>
      
      - (void)dismissImageViewer;
      
      @end
      
      @implementation OSImageViewController
      
      - (void)loadView
      {   //LOG(@"loadView called");
          scrollView = [[ImageScrollView alloc] init];
      
          scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
      
          UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewDoubleTapped:)];
          doubleTapRecognizer.numberOfTapsRequired = 2;
          doubleTapRecognizer.numberOfTouchesRequired = 1;
          [scrollView addGestureRecognizer:doubleTapRecognizer];
      
          self.view = scrollView;
      }
      
      - (BOOL)prefersStatusBarHidden {
          return NO;
      }
      
      - (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
          //LOG(@"scrollViewDoubleTapped called");
          [self.delegate dismissImageViewer];
      }
      
      @end
      
      
      @implementation ViewController
      
      -(void)browseImage:(UIImage*)image
      {
      
          OSImageViewController *_imageViewerController = [[OSImageViewController alloc] init];
          UIImage *img = [[UIImage alloc] initWithData:UIImagePNGRepresentation(image)];
      
          _imageViewerController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
          _imageViewerController.modalPresentationStyle = UIModalPresentationFullScreen;
          _imageViewerController.delegate = self;
          [self presentViewController:_imageViewerController animated:YES completion:^(void){
              [_imageViewerController setImage:img];
      
          }];
      
      }
      - (void)dismissImageViewer {
          [self dismissViewControllerAnimated:YES completion:nil];
      }
      
      @end
      

      【讨论】:

        【解决方案4】:

        我相信你想从你的 ViewController 中显示一个模态视图。 让模态视图由 ViewController2 管理。在 ViewController2.h 中声明 ViewController2 的协议

        @protocol viewController2Delegate 
        
        -(void)dismissViewController2;
        
        @end
        

        现在让 ViewController 实现这个协议

        @interface ViewController : UIViewController <updateUIDelegate,viewController2Delegate>
        

        将方法添加到 ViewController.m

        -(void)dismissViewController2
        {
             [self dismissViewControllerAnimated:YES completion:nil];
        }
        

        现在,每当您从 ViewController 推送模态视图(由 ViewController2 管理)时,您都将委托设置为 self。您的 ViewController.m 代码可能如下所示

        ViewController2 *objViewController2 = [[ViewController2 alloc]init];
        objViewController2.delegate = self;
        [self presentViewController:objViewController2 animated:YES completion:nil];
        

        希望这能解决你的问题

        【讨论】:

        • 谢谢!我会试试这个。我刚刚意识到我不正确地声明了多重授权。你知道吗,如果我以这种方式关闭一个模态,我是否可以在执行后台代码时同时打开另一个模态(假设我想在模态关闭时做一些事情)?
        • @Dave 你可以使用dismissViewController的完成块如下[self dismissViewControllerAnimated:YES completion:^() { //Add the code to open another modal view }];
        • 谢谢!如果我要调用另一个视图,那将是调用 prepareForSegue 方法的好地方吗?
        • @Dave 是的,如果您想在第一个视图控制器被解雇后呈现下一个视图控制器。另一种方法是您不需要等待完成 og 解雇,您可以立即在委托方法中编写代码,而不是在完成块中编写它,但效果将是不可预测的(您不会有总没有完成块的控制)
        【解决方案5】:

        沟通模式

        委托是一种或多或少松散耦合的对象用来相互通信的通信模式。 iOS 框架提供以下模式:KVO、Notification、Delegation、Block、Target-Action。

        通常,在某些情况下,选择归结为品味问题。但是,有很多案例非常明确。

        还需要注意的是,每种模式的使用都会导致通信过程中涉及的对象之间存在一定程度的耦合。

        现在让我们关注委托、阻止、目标动作

        代表团

        耦合程度(与互不了解的程度成正比):松散

        它允许我们自定义对象的行为(装饰)并收到有关某些事件的通知(回调)。在这种情况下,耦合是相当松散的,因为 sender 只知道它的 delegate 符合某个 protocol

        由于委托协议可以定义任意方法,因此您可以完全根据需要对通信进行建模。您可以以方法参数的形式传递有效负载,委托甚至可以根据委托方法的返回值进行响应。委托是一种非常灵活和直接的方式,可以在两个对象之间建立某种盲目的通信,出于设计原因应该松散耦合。让我们考虑一下 tableview 和它的 dataSource 委托之间的通信机制。

        相反,如果两个对象彼此紧密耦合,一个没有另一个就无法运行,则无需定义委托协议(改用组合)。在这些情况下,对象可以知道对方的类型并直接相互交谈。这方面的两个现代例子是UICollectionViewLayoutNSURLSessionConfiguration

        目标行动

        耦合程度:非常松散

        Target-Action 是用于发送消息以响应用户界面事件的典型模式。 iOS 上的 UIControl 和 Mac 上的 NSControl/NSCell 都支持这种模式。 Target-Action 在消息的发送者和接收者之间建立了一个非常松散的耦合。消息的收件人不知道发件人,甚至发件人也不必事先知道收件人是什么。如果目标为 nil,则动作将沿着响应者链向上移动,直到找到响应它的对象。在 iOS 上,每个控件甚至可以与多个目标-动作对相关联。

        基于目标操作的通信的一个限制是发送的消息不能携带任何自定义负载。在 Mac 上,动作方法总是接收发送者作为第一个参数。在 iOS 上,它们可以选择接收发送者和触发动作的事件作为参数。但除此之外,没有办法让控件通过操作消息发送其他对象。

        屏蔽

        块通常用于向对象传递要在其生命周期结束之前执行的行为。此外,他们还可以用与可能创建保留周期相关的警告替换代表。

        self.tableView.didSelectRowAtIndexPath = ^(NSIndexPath *indexPath) { 
            ...
            [self.tableView reloadData];
            ...
        }
        

        在这种情况下,发送者保留其选择块保留发送者的表视图,因此我们最好使用委托模式。

        块通信大放异彩的一个例子:

        self.operationQueue = [[NSOperationQueue alloc] init]
        Operation *operation = [[Operation alloc] init];
        operation.completionBlock = ^{
            [self finishedOperation]
        }
        [operationQueue addOperation:operation];
        

        上面的代码中也有一个retain循环,但是一旦队列移除操作,retain循环就被打破了。

        如果我们调用的消息必须发送回特定于该方法调用的一次性响应,那么块非常适合,因为这样我们可以打破潜在的保留周期。此外,如果让代码与消息调用一起处理消息有助于提高可读性,那么很难反对使用块。按照这些思路,块的一个非常常见的用例是完成处理程序、错误处理程序等。

        帮助我们做出正确选择的图表

        来源:objc.io

        在您的具体情况下,我会使用 target-action 通信模式来关闭呈现的模式视图控制器。

        例如,

         ModalViewController *modalViewController = [[ModalViewController alloc] init];
         [self presentViewController:modalViewController animated:YES completion:^{
            [modalViewController.closeButton addTarget:self action:@selector(dismissModalViewControllerAnimated:)
                                      forControlEvents:UIControlEventTouchUpInside];
        }];
        

        【讨论】:

          猜你喜欢
          • 2022-11-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多