所以我已经开始深入研究用于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:@","]]];
}
这允许我在需要时通过将这些方法作为消息发送到self.delegate
类中的UserDecision
来调用这些方法。
这是我目前的设置。我的问题已经变成了我想要创建一个最终弹出的模态seque视图(在用户按下按钮以显示视图之后),然后可以将其解除。我遇到的问题是,这种观点,从我在网上做的阅读和研究,只能通过授权来解除,除非我想让事情变得讨厌。现在,我尝试在我的课程中实现这些信息,但后来我读到一个类只能是另一个类的委托。由于我的ViewController
(这是我的主窗口)已经是我的UserDecision
类的委托,我不能使它成为我创建的新视图的委托,因此无法解雇风景。所以,我在这里请求你的帮助。我该如何解决这个问题?
另外,对于我的更多代码,如果你想看一下,这里有一个指向我的gitHub的链接:https://github.com/Aghassi/Xcode/tree/master/Bubble%20Tea%20Choice/Bubble%20Tea%20Choice
答案 0 :(得分:3)
我读到一个类只能是另一个类的委托。和 因为我的ViewController(这是我的主窗口)已经是一个委托 我的UserDecision类,我不能使它成为新的View I的委托 已创建
我不相信这是真的。您可以使ViewController实现许多不同的协议,因此委托给不同的类/对象。
例如:( UITableViewDelegate和UITextViewDelegate都可以在同一个ViewController上为2个独立的对象(UITextView和UITableView)实现。
至于使用委托关闭模态窗口,另一种选择是使用blocks。
答案 1 :(得分:2)
viewController可以解除自身。只需将一个dismiss按钮挂钩到一个调用类似函数的函数:
[self.presentingViewController dismissViewControllerAnimated:YES];
或
[self.navigationController popViewControllerAnimated:YES];
解雇可以使用委托模式完成,但并非一切都是必需的。
答案 2 :(得分:1)
您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
答案 3 :(得分:1)
我相信你想从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];
希望这能解决您的问题
答案 4 :(得分:1)
沟通模式
委派是或多或少松散耦合的对象用来相互通信的通信模式之一。 iOS Framework提供以下模式: KVO,通知,委派,阻止,目标 - 行动。
一般来说,有些情况下选择归结为品味问题。但是,有很多案例非常明确。
同样重要的是要注意,每种模式的使用都会导致参与通信过程的对象之间产生一定程度的耦合。
现在让我们关注授权,阻止,目标行动。
<强> DELEGATION 强>
耦合程度(与相互无知的程度成正比):松散
它允许我们自定义对象的行为(装饰)并通知某些事件(回调)。在这种情况下,耦合非常松散,因为 sender 只知道其委托符合某个协议。
由于委托协议可以定义任意方法,因此您可以根据需要对通信进行建模。您可以以方法参数的形式移交有效负载,并且委托甚至可以根据委托方法的返回值进行响应。委托是一种非常灵活和直接的方式,可以在两个对象之间建立某种盲目的通信,这两个对象应该由于设计原因而松散耦合。让我们考虑一个tableview和它的dataSource委托之间的通信机制。
相反,如果两个对象彼此紧密耦合,而另一个对象无法正常工作,则无需定义委托协议(而是使用组合)。在这些情况下,对象可以知道对方的类型并直接相互通信。两个现代的例子是UICollectionViewLayout
和NSURLSessionConfiguration
。
<强> TARGET-ACTION 强>
耦合程度:非常松散
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];
上面的代码中也有一个保留周期,但是一旦队列删除了操作,保留周期就会中断。
如果我们调用的消息必须发回特定于此方法调用的一次性响应,则块非常适合,因为这样我们就可以打破潜在的保留周期。此外,如果它有助于将代码与消息调用一起处理消息的可读性,则很难反对使用块。沿着这些方向,块的一个非常常见的用例是完成处理程序,错误处理程序等。
帮助我们做出正确选择的图表
来源:objc.io
在您的具体情况下,我会使用目标操作通信模式来关闭所呈现的模态视图控制器。
例如,
ModalViewController *modalViewController = [[ModalViewController alloc] init];
[self presentViewController:modalViewController animated:YES completion:^{
[modalViewController.closeButton addTarget:self action:@selector(dismissModalViewControllerAnimated:)
forControlEvents:UIControlEventTouchUpInside];
}];