XWInteractiveTransition.m 8.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
//
//  XWInteractiveTransition.m
//  XWTransitionDemo
//
//  Created by wazrx on 16/6/10.
//  Copyright © 2016年 wazrx. All rights reserved.
//

#import "XWInteractiveTransition.h"
#import <objc/runtime.h>

typedef struct {
    unsigned int willBegin :      1;
    unsigned int isUpdating :     1;
    unsigned int willBeginTimer : 1;
    unsigned int willEnd :        1;
} delegateFlag;

@interface XWInteractiveTransition ()<UIGestureRecognizerDelegate>
@property (nonatomic, assign) XWInteractiveTransitionGestureDirection direction;
@property (nonatomic, copy) void(^config)(CGPoint startPoint);
@property (nonatomic, strong) CADisplayLink *timer;
@property (nonatomic, assign) CGFloat percent;
@property (nonatomic, assign) CGFloat timeDis;
@property (nonatomic, assign) delegateFlag delegateFlag;
@property (nonatomic, assign) BOOL vertical;
@property (nonatomic, assign) CGFloat edgeSpacing;


@end

@implementation XWInteractiveTransition

+ (instancetype)xw_interactiveTransitionWithDirection:(XWInteractiveTransitionGestureDirection)direction config:(void(^)(CGPoint startPoint))config edgeSpacing:(CGFloat)edgeSpacing{
    return [[self alloc] _initInteractiveTransitionWithDirection:direction config:config edgeSpacing:edgeSpacing];
}

- (instancetype)_initInteractiveTransitionWithDirection:(XWInteractiveTransitionGestureDirection)direction config:(void(^)(CGPoint startPoint))config edgeSpacing:(CGFloat)edgeSpacing{
    self = [super init];
    if (self) {
        _config = config;
        _direction = direction;
        _edgeSpacing = edgeSpacing;
        _vertical = direction == XWInteractiveTransitionGestureDirectionDown || direction == XWInteractiveTransitionGestureDirectionUp;
    }
    return self;
}

- (void)setDelegate:(id<XWInteractiveTransitionDelegate>)delegate{
    _delegate = delegate;
    _delegateFlag.willBegin = _delegate && [_delegate respondsToSelector:@selector(xw_interactiveTransitionWillBegin:)];
    _delegateFlag.isUpdating = _delegate && [_delegate respondsToSelector:@selector(xw_interactiveTransition:isUpdating:)];
    _delegateFlag.willBeginTimer = _delegate && [_delegate respondsToSelector:@selector(xw_interactiveTransitionWillBeginTimerAnimation:)];
    _delegateFlag.willEnd = _delegate && [_delegate respondsToSelector:@selector(xw_interactiveTransition:willEndWithSuccessFlag:percent:)];
}

- (void)xw_addPanGestureForView:(UIView *)view to:(BOOL)flag{
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(_xw_handleGesture:)];
    //为drawerAnimator记录pan手势
    pan.delegate = self;
    NSString * panType = flag ? @"xw_interactiveToPan" : @"xw_interactiveBackPan";
    objc_setAssociatedObject(pan, "xw_interactivePanKey", panType, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    [view addGestureRecognizer:pan];
}

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
    if (_edgeSpacing <= 0) {
        return YES;
    }
    CGPoint point = [gestureRecognizer locationInView:gestureRecognizer.view];
    BOOL should = NO;
    switch (_direction) {
        case XWInteractiveTransitionGestureDirectionLeft: {
            should = point.x >= gestureRecognizer.view.frame.size.width - _edgeSpacing;
            break;
        }
        case XWInteractiveTransitionGestureDirectionRight: {
            should = point.x <= _edgeSpacing;
            break;
        }
        case XWInteractiveTransitionGestureDirectionUp: {
            should = point.y >= gestureRecognizer.view.frame.size.height - _edgeSpacing;
            break;
        }
        case XWInteractiveTransitionGestureDirectionDown: {
            should = point.y <= _edgeSpacing;
            break;
        }
    }
    return should;
}

/**
 *  手势过渡的过程
 */
- (void)_xw_handleGesture:(UIPanGestureRecognizer *)panGesture{
    switch (panGesture.state) {
        case UIGestureRecognizerStateBegan:
            if (_delegateFlag.willBegin) {
                [_delegate xw_interactiveTransitionWillBegin:self];
            }
            CGPoint startPoint = [panGesture locationInView:panGesture.view];
            _interation = YES;
            if (_config) {
                _config(startPoint);
            }
            [self _xw_caculateMovePercentForGesture:panGesture];
            break;
        case UIGestureRecognizerStateChanged:{
            [self _xw_caculateMovePercentForGesture:panGesture];
            [self _xw_updatingWithPercent:_percent];
            break;
        }
        case UIGestureRecognizerStateEnded:{
            //判断是否需要timer
            if (!_timerEable) {
                _percent >= 0.5 ? [self _xw_finish] : [self _xw_cancle];
                return;
            }
            //判断此时是否已经转场完成,大于1或者小于0
            BOOL canEnd = [self _xw_canEndInteractiveTransitionWithPercent:_percent];
            if (canEnd) return;
            //开启timer
            [self _xw_setEndAnimationTimerWithPercent:_percent];
            break;
        }
        default:
            break;
    }
}

- (BOOL)_xw_canEndInteractiveTransitionWithPercent:(CGFloat)percent{
    BOOL can = NO;
    if (percent >= 1) {
        [self _xw_finish];
        can = YES;
    }
    if (percent <= 0) {
        [self _xw_cancle];
        can = YES;
    }
    return can;
}

- (void)_xw_caculateMovePercentForGesture:(UIPanGestureRecognizer *)panGesture{
    static CGFloat baseValue = 0.0f;
    baseValue = _panRatioBaseValue > 0 ? _panRatioBaseValue : _vertical ? panGesture.view.frame.size.height : panGesture.view.frame.size.width;
    //手势百分比
    switch (_direction) {
        case XWInteractiveTransitionGestureDirectionLeft:{
            CGFloat transitionX = -[panGesture translationInView:panGesture.view].x;
            _percent += transitionX / baseValue;
        }
            break;
        case XWInteractiveTransitionGestureDirectionRight:{
            CGFloat transitionX = [panGesture translationInView:panGesture.view].x;
            _percent += transitionX / baseValue;
        }
            break;
        case XWInteractiveTransitionGestureDirectionUp:{
            CGFloat transitionY = -[panGesture translationInView:panGesture.view].y;
            _percent += transitionY / baseValue;
        }
            break;
        case XWInteractiveTransitionGestureDirectionDown:{
            CGFloat transitionY = [panGesture translationInView:panGesture.view].y;
            _percent += transitionY / baseValue;
        }
            break;
    }
    [panGesture setTranslation:CGPointZero inView:panGesture.view];
}

//设置开启timer
- (void)_xw_setEndAnimationTimerWithPercent:(CGFloat)percent{
    _percent = percent;
    //根据失败还是成功设置刷新间隔
    if (percent > 0.5) {
        _timeDis = (1 - percent) / ((1 - percent) * 60);
    }else{
        _timeDis = percent / (percent * 60);
    }
    if (_delegateFlag.willBeginTimer) {
        [_delegate xw_interactiveTransitionWillBeginTimerAnimation:self];
    }
    //开启timer
    [self _xw_startTimer];
}

- (void)_xw_finish{
    if (_delegateFlag.willEnd) {
        [_delegate xw_interactiveTransition:self willEndWithSuccessFlag:YES percent:_percent];
    }
    [self finishInteractiveTransition];
    _percent = 0.0f;
    _interation = NO;
}

- (void)_xw_cancle{
    if (_delegateFlag.willEnd) {
        [_delegate xw_interactiveTransition:self willEndWithSuccessFlag:NO percent:_percent];
    }
    [self cancelInteractiveTransition];
    _percent = 0.0f;
    _interation = NO;
}

- (void)_xw_updatingWithPercent:(CGFloat)percent{
    [self updateInteractiveTransition:percent];
    if (_delegateFlag.isUpdating) {
        [_delegate xw_interactiveTransition:self isUpdating:_percent];
    }
}

- (void)_xw_startTimer{
    if (_timer) {
        return;
    }
    _timer = [CADisplayLink displayLinkWithTarget:self selector:@selector(_xw_timerEvent)];
    [_timer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
}

- (void)_xw_stopTimer{
    if (!_timer) {
        return;
    }
    [_timer invalidate];
    _timer = nil;
    
}

//timer 事件
- (void)_xw_timerEvent{
    if (_percent > 0.5) {
        _percent += _timeDis;
    }else{
        _percent -= _timeDis;
    }
    //通过timer不断刷新转场进度
    [self _xw_updatingWithPercent:_percent];
    BOOL canEnd = [self _xw_canEndInteractiveTransitionWithPercent:_percent];
    if (canEnd) {
        [self _xw_stopTimer];
    }
}
@end