IBAction in UIView - Compatible with MVC?

2019-09-19 01:16发布

问题:

I'm trying to understand the implementation of the MVC-Pattern in iOS Apps with Objective-C.

Right now I have a DataProvider object which holds and provides my data to other objects in my application (Model).

Furthermore I have a View (.xib) with an additional class (UIView; lets call it "ViewClass"). I told my view to be a subclass of my ViewClass so that I'm able to create IBOutlets to my subviews like labels and IBActions to my buttons. So this is obviously my View in the MVC-Pattern.

Last but not least, I have a ViewController object which has a reference to my DataProvider and which holds an object of my View. When the ViewController is loaded into memory, it instances the View in viewDidLoad and adds it to its own view by calling addSubview:(UIView *)view. This is my Controller in the MVC-Pattern.

So far so good, but what I do not understand is the following. According to the MCV-Pattern, the View should only display data provided by the ViewController which gets the data from the Model. Any logical operation should only happen in the ViewController as it is responsible for this kind of tasks. But if I create an IBAction in my "ViewClass", I'm forced to have some kind of programming logic in my View.

Lets say I would like to submit a form with data the user entered in some textfield by clicking a button. In my ViewClass I have an IBOutlet to the textfield to be able to reference it later and read the data the user entered. And I have an IBAction to the button to be able to catch the click event. The code to send the data is now written in the IBAction Method in my View so I have program logic in my View.

To my question: Isn't this a breach against the MVC-Pattern?

I could possibly pass a reference of my ViewController to my View while I create an instance of the View and then call a method of my ViewController in the IBAction which then gets and sends the data. But I can't really imagine that this is the right approach. Or do I understand something wrong?

Thanks in advance.

回答1:

Not sure if I understand your view hierarchy correctly, but I wouldn't recommend to put any logic that belongs to the model into the view.

I would make the ViewClass a subclass of UIControl so it can send events itself. And if the button was pressed you send a valueChangedEvent which is received by the viewController.

e.g:

// .h
@interface ViewClass : UIControl
@property (readonly, nonatomic) UIButton *button;   // read only in public interface
@property (readonly, nonatomic) UITextField *textField;
@end

// .m
@interface ViewClass ()
@property (strong, nonatomic) UIButton *button;     // readwrite in private interface
@property (strong, nonatomic) UITextField *textField;
@end

@implementation ViewClass
- (IBAction)buttonPressed:(UIButton *)sender {

    // send valueChanged event to target
    [self sendActionsForControlEvents:UIControlEventValueChanged];
}

@end

and your viewController:

- (void)viewDidLoad {
    [super viewDidLoad];
    ViewClass *myView = ...
    [myView addTarget:self action:@selector(myViewDidChangeValue:) forControlEvents:UIControlEventValueChanged];
}

- (IBAction)myViewDidChangeValue:(ViewClass *)sender {
    NSLog(@"Send \"%@\" to server", sender.textField.text);
}

And you can use addTarget:forControlEvents: for subviews of a view as well. So you can actually connect the button of ViewClass to your viewController, you just might not be able to do it in Interface Builder.

[myView.button addTarget:self action:@selector(viewButtonTapped:) forControlEvents:UIControlEventTouchUpInside];