5

根据 VIPER 设计模式,UIView 动画代码应该放在哪里?

它应该在视图中还是在演示者中?

笔记:

我有一个CustomView我希望在触摸后在屏幕上移动的东西。

CustomView添加到我的屏幕中ViewController

4

2 回答 2

3

You should place it in [R]outer. It's well described in this article:

Since the Presenter contains the logic to react to user inputs, it is the Presenter that knows when to navigate to another screen, and which screen to navigate to. Meanwhile, the wireframe knows how to navigate. So, the Presenter will use the wireframe to perform the navigation. Together, they describe a route from one screen to the next.

The wireframe is also an obvious place to handle navigation transition animations.

Note that they called Router as Wireframe.

Update (Answer corrections based on updates of question and comments)

I’ll give my opinion based on these articles. Lets consider two cases: simple and more sophisticated. In simple case the task is just change the view's position to predefined one with animation(i.e. change the state). And in more sophisticated case the task is change the position of custom view based on coordinates of touch.

Take a look at the simple case. You have a [V]iewController which contains custom view. ViewController adopts ViperView protocol:

@protocol VIPERView
- (void)changeCustomViewState;
@end

@interface ViewController : UIViewController<VIPERView>
@end

In implementation we have something like this:

@implementation ViewController {
    BOOL _isInitialState;
    IBOutlet UIView *_customView;
}

- (void)changeCustomViewState
{
    _isInitialState = !_isInitialState;
    [self changeCustomViewPositionAnimated:YES];
}

- (void)changeCustomViewPositionAnimated:(BOOL)animated
{
    void(^performChange)() = ^() {
        if (_isInitialState)
            _customView.center = CGPointMake(50, 50);
        else
            _customView.center = CGPointMake(250, 250);
    };

    if (animated)
    {
        [UIView animateWithDuration:[CATransaction animationDuration] animations:^{
            performChange();
        }];
    }
    else
    {
        performChange();
    }
}

As view's responsibilities according to VIPER are "display information to the user" and "detect user interaction” and it isn't allowed to make decisions what to do after touch except to notify [P]resenter about this event. Presenter in its turn make decision what do and calls

[self.viperView  changeCustomViewState];

Thus actual code that performs animation located in [V]iew but [P]resenter triggers its execution (because its responsibilities are "tell the view what to display" and "Handle events"). Defining position of custom view is jsut part of view’s layout. So it’s part of configuration. Presenter just turns it in animated way.

In more sophisticated case we’ll consider changing of custom view’s position depending on touch location. Our task is to change the position of view after touch in such way that it remains on screen. For example if view is located in bottom left angle of screen, the touch shouldn’t move it below screen's bounds or behind the left side of screen. It should move the view in one of three free corners. In this case our protocol for view will looks something like this:

@protocol VIPERView
// @param - related position to the screen bounds in range (0;1)
- (void)changeCustomViewRelatedPosition:(CGPoint)point animated:(BOOL)animated;
@end

and in implementation

@implementation ViewController {
    IBOutlet UIView *_customView;
}

- (void)changeCustomViewRelatedPosition:(CGPoint)point animated:(BOOL)animated
{
     CGPoint thisCoordinateSpacePoint = // translate ‘point’ to screen’s coordinate space ;
    void(^performChange)() = ^() {
        _customView.center = thisCoordinateSpacePoint;
    };

    if (animated)
    {
        [UIView animateWithDuration:[CATransaction animationDuration] animations:^{
            performChange();
        }];
    }
    else
    {
        performChange();
    }
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    CGPoint point = //determine location
    [self.viperPresenter userDidTouchViewWithPosition:point];
}

@end

For [P]resenter we need to define protocol in this case:

@protocol VIPERPresenter
- (void)userDidTouchViewWithPosition:(CGPoint)point;
@end

When user touches the screen the view calls presenter to notify about certain event:

[self.viperPresenter userDidTouchViewWithPosition:point];

As article states:

The presenter is notified of events by the view and part of its job is to handle those events accordingly. This usually means asking the interactor to retrieve some bit of information or carry out some task.

In our case app needs to determine coordinates where the view should be moved. This algorithm could be encapsulated and be interchangeable. So we could retrieve this algorithm from different places: database, server, etc. To accomplish this task we use [I]nteractor:

 // in Presenter class
[self.viperInteractor handleCoordinates:(CGPoint)point];

Then [I]nteractor asks DataManager to map these coordinates to new ones somehow using algorithm from the [E]ntity (should it be moved in upper right or upper left corner) and notifies back [P]resenter with new coordinates(the corner view should move). And finally Presenter performs:

[self.viperView changeCustomViewRelatedPosition:newPosition];

Again, animation code is placed inside [V]iew as part of layout. But the decision(exact parameters) are defined by other components(Presenter, Interactor, Entity)

于 2016-01-27T15:48:17.867 回答
-1

它应该放在 View (ViewController) 中你可以阅读https://www.objc.io/issues/13-architecture/viper/中的内容

于 2016-01-27T15:52:12.690 回答