实现“手机qq”侧滑菜单 -- 吴欧
阅读原文时间:2023年07月10日阅读:2

基本数据采集

经过体验,手机QQ采用的应该是线性动画,即视图缩放比例等随手指在屏幕上滑动的距离以一次方程的形式变化。

提取基本数据,向右侧滑达到最大幅度时:

1、   右侧主视图左边界距离屏幕左边界的距离占屏幕宽度的比例为:78%

2、   右侧主视图的高度占屏幕高度的比例为:77%

分步实现:

1、实现主视图的缩放侧滑;

2、实现主视图与左视图的联动;

第一步,实现主视图的缩放侧滑

此前动手做时参考了一些类似的demo,发现许多是用手势UIPanGestureRecognizer来实现的,而本文将采用UITouch。并使用以下两个触摸触发事件:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

  • (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event

原理:

主视图mainVC的移动和缩放:

①. 主视图frmae的x坐标 = 主视图frmae的x坐标 + 手指滑动的x轴总偏移量偏移量;

#pragma mark - 手指在屏幕上移动

  • (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
    {

    if ([event touchesForView:_mainVC.view]) {

    // 获取UITouch对象  
    UITouch \*touch = \[touches anyObject\];
    
    // 获取当前点  
    CGPoint currentPoint = \[touch locationInView:self.view\];
    
    // 获取上一个点  
    CGPoint prePoint = \[touch previousLocationInView:self.view\];
    
    // x轴偏移量:当手指移动一点的时候,x偏移多少  
    CGFloat offsetX = currentPoint.x - prePoint.x;
    
    // 设置当前主视图的frame  
    \_mainVC.view.frame = \[self getCurrentFrameWithOffsetX:offsetX\];
    
    // 移动渐变效果 (明 - 暗)  
    \_blackCover.alpha = (1 - \_mainVC.view.frame.origin.x / RTarget);

    }

    // 判断是拖拽 还是 点击tap
    _isDraging = YES;
    }

②. 主视图的缩放比例 :

// 当手指偏移一点,根据X轴的偏移量算出当前主视图的frame

  • (CGRect)getCurrentFrameWithOffsetX:(CGFloat)offsetX
    {

    // 获取y轴偏移量,手指每移动一点,y轴偏移多少
    CGFloat offsetY = offsetX * _maxOffestHight / screenWidth;

    // 每次移动缩小比例
    CGFloat scale = (screenHeight - 2 * offsetY) / screenHeight;

#if 0
if (offsetX < 0 && _mainVC.view.frame.origin.x <= 0)
{

    // 往左边滑动  
    scale = (screenH + 2 \* offsetY) / screenH;

}  

#endif

// 获取之前的frame \*\*\*\*\*\*\*\*\*\*\*\*\*\*\*  限制成只能显示左视图!  
CGRect frame = \_mainVC.view.frame;

if ((frame.origin.x+offsetX) >=0 )  
    frame.origin.x += offsetX;  
else  
    frame.origin.x = 0;

frame.size.height = frame.size.height \*scale;  
frame.size.width = frame.size.width \*scale;  
frame.origin.y = (screenHeight - frame.size.height) / 2.0;

return frame;  

}

/**  当手指偏移一点,根据X轴的偏移量算出当前主视图的frame **/

// 获取y轴偏移量,手指每移动一点,y轴偏移多少

CGFloat offsetY = offsetX * _maxOffestHight / screenWidth;

// 每次移动缩小比例

CGFloat scale = (screenHeight - 2 * offsetY) / screenHeight;

CGRect frame = _mainVC.view.frame;

… …

/** 主视图的位置变化 和 大小缩放 **/

frame.origin.x += offsetX;

frame.size.height = frame.size.height *scale;

frame.size.width = frame.size.width *scale;

frame.origin.y = (screenHeight - frame.size.height) / 2.0;

… …

第二步,实现主视图与左视图的联动

原理:

重点是找出线性关系,然后联动可以这样做 :

1、这是leftVC.view的缩放比例:

找出这两点 (0.77 ,0) (1 ,screenwidth
* 0.78),即(left.view的缩放比例, main.view.x坐标),可得线性关系:

CGFloat leftScale = ((1 - _minSclae)/(screenWidth * _boundScale))*_mainVC.view.frame.origin.x + _minSclae;
leftCX = _mainVC.view.frame.origin.x >= screenWidth * _boundScale ? self.view.center.x : leftCX;

2、这是leftVC.view的移动:

找出这两点(self.view.center.x ,
screenwidth * 0.78) (center - 80 , 0),即(屏幕中心点x的坐标 ,main.view.or.x坐标),可得线性关系:

CGFloat leftCX = ( \_leftCenterFactor / (screenWidth \* \_boundScale) )\*\_mainVC.view.frame.origin.x + screenWidth/2 - \_leftCenterFactor;

leftScale = leftScale >= 1 ? 1 :leftScale;

3、最后移动和缩放:

_leftVC.view.center = CGPointMake(leftCX, self.view.center.y);

\_leftVC.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, leftScale, leftScale);

完整代码:

/**
* 开启左视图联动事件
*/

  • (void)startedLeftViewLinkage
    {
    // 执行左视图联动动画

    // (self.view.center.x , screenwidth * 0.78) (center - 80 , 0) ==> (中心点x的坐标 ,main.view.or.x坐标),线性关系
    CGFloat leftCX = ( _leftCenterFactor / (screenWidth * _boundScale) )*_mainVC.view.frame.origin.x + screenWidth/2 - _leftCenterFactor;
    leftCX = _mainVC.view.frame.origin.x >= screenWidth * _boundScale ? self.view.center.x : leftCX;

    // (0.77 , 0) (1 , screenwidth*0.78) ==》 (left.view的缩放比例, main.view.or.x坐标),线性关系
    CGFloat leftScale = ((1 - _minSclae)/(screenWidth * _boundScale))*_mainVC.view.frame.origin.x + _minSclae;
    leftScale = leftScale >= 1 ? 1 :leftScale;

    _leftVC.view.center = CGPointMake(leftCX, self.view.center.y);
    _leftVC.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, leftScale, leftScale);
    }

最后,松开手指事件:

#pragma mark - 停止移动

  • (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
    {
    if ([event touchesForView:_mainVC.view]) {

    // 点击tap事件  复位满屏  
    if (\_isDraging == NO && \_mainVC.view.frame.origin.x != 0) {
    \[UIView animateWithDuration:0.25 animations:^{
    
        \_mainVC.view.frame = self.view.bounds;  
    }\];  
    } CGFloat target = 0; if (\_mainVC.view.frame.origin.x > screenWidth / 3) {
    // 定位到右边  
    target = RTarget;
    }else if (CGRectGetMaxX(\_mainVC.view.frame) < screenWidth / 3) {
    // 定位到左边  
    target = LTarget;
    } // 停止拖拽时判断是显示左视图还是主视图 \[UIView animateWithDuration:0.25 animations:^{
    if (target) { // 在需要定位左边或者右边
    
        // 获取x轴偏移量  
        CGFloat offsetX = target - \_mainVC.view.frame.origin.x;
    
        // 设置当前主视图的frame  
        \_mainVC.view.frame = \[self getCurrentFrameWithOffsetX:offsetX\];  
        \_blackCover.alpha = (1 - \_mainVC.view.frame.origin.x / RTarget);
    
    }else{
    
        // 还原  
        \_mainVC.view.frame = self.view.bounds;  
        \_blackCover.alpha = (1 - \_mainVC.view.frame.origin.x / RTarget);  
    }
    
    // 开启左视图联动  
    \[self startedLeftViewLinkage\];  
    }\];

    }

    _isDraging = NO;
    }

以上是对左视图和主视图的移动及缩放关系的解析,侧滑的关键就是找准视图之间的内在动态联系。按本文方法可达到高仿,实现的效果基本与手机QQ一样。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章