基本数据采集

经过体验,手机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一样。

 

实现“手机qq”侧滑菜单 -- 吴欧的更多相关文章

  1. 再造 “手机QQ” 侧滑菜单(三)——视图联动

    代码示例:https://github.com/johnlui/SwiftSideslipLikeQQ 本 文中,我们将一起使用 UINavigationController 来管理主视图,并实现点击 ...

  2. 再造 “手机QQ” 侧滑菜单(一)——实现侧滑效果

    本系列文章中,我们将尝试再造手机QQ的侧滑菜单,力争最大限度接近手Q的实际效果,并使用 Auto Layout 仿造左侧菜单,实现和主视图的联动. 代码示例:https://github.com/jo ...

  3. 再造 “手机QQ” 侧滑菜单(二)——高仿左视图

    代码示例:https://github.com/johnlui/SwiftSideslipLikeQQ 本篇文章中,我们将一起使用 Auto Layout 高仿手Q的左侧视图,力争达成从布局到动画的全 ...

  4. 自定义控件?试试300行代码实现QQ侧滑菜单

    Android自定义控件并没有什么捷径可走,需要不断得模仿练习才能出师.这其中进行模仿练习的demo的选择是至关重要的,最优选择莫过于官方的控件了,但是官方控件动辄就是几千行代码往往可能容易让人望而却 ...

  5. iOS仿QQ侧滑菜单、登录按钮动画、仿斗鱼直播APP、城市选择器、自动布局等源码

    iOS精选源码 QQ侧滑菜单,右滑菜单,QQ展开菜单,QQ好友分组 登录按钮 3分钟快捷创建高性能轮播图 ScrollView嵌套ScrolloView(UITableView .UICollecti ...

  6. Swift实战-小QQ(第2章):QQ侧滑菜单

    QQ侧滑实现架构:需要建立以下几个ViewController:1.XQBaseViewController 2.LeftViewController3.RightViewController4.Co ...

  7. 仿QQ侧滑菜单<大自然的搬运工-代码不是我的>

    1.记录下效果图 2.二个工具类 package myapplication.com.myapplicationfortest.utils; import android.util.Log; /** ...

  8. 类似QQ侧滑菜单功能实现

    之前的那文章简单实现了菜单侧拉功能,但是做不到像QQ那样导航条和tabBar一起移动...之后在网上找资料,有了思路,就自个写了个demo试试水. 先创建QHLMainController控制器,并把 ...

  9. css3实现手机qq空间菜单按钮

    工作之余写的一个类似于QQzone的菜单效果 先上截图: 图一为点击按钮前界面: 图二为点击按钮后的界面 下面上代码: <!--css部分--> <style type=" ...

随机推荐

  1. css 基础 rgba表示法

    color:rgba(); //r表示red 红色 //g表示green 绿色 //b表示blue 蓝色 //a 表示透明度 color:rgb(0,0,0,0) //黑色 color:rgb(255 ...

  2. 第10组 Beta冲刺 (3/5)

    1.1基本情况 ·队名:今晚不睡觉 ·组长博客:https://www.cnblogs.com/cpandbb/p/14018630.html ·作业博客:https://edu.cnblogs.co ...

  3. Go语言系列之标准库os

    os包提供了操作系统的系列函数,这些接口不依赖平台.设计为Unix风格的,错误处理是go风格的:调用失败会返回错误值而非错误码.通常错误值里包含更多信息. os包的接口在所有操作系统中都是一致的.非公 ...

  4. Go语言系列之日志库zap

    在许多Go语言项目中,我们需要一个好的日志记录器能够提供下面这些功能: 能够将事件记录到文件中,而不是应用程序控制台. 日志切割-能够根据文件大小.时间或间隔等来切割日志文件. 支持不同的日志级别.例 ...

  5. testng 的常用注解

    常用注解如下: @BeforeSuite: 此注解的方法会在当前测试集合中的任一测试用例前执行 @AfterSuite: 此注解的方法会在当前测试集合中的所有测试程序结束后执行 @BeforeTest ...

  6. Linux命令(2)--cp拷贝、mv剪切、head、tail追踪、tar归档

    文章目录 一.知识回顾 ls cd 二.Linux基本操作(二) 1.cp 拷贝 2.mv 移动(剪切) 3.head 头部 4.tail 追踪(尾部) 5.tar 归档 查看 压缩 解压 总结 一. ...

  7. java原码、反码、补码、位运算

    1.对于有符号的数(java中的数都是有符号的) 二进制的最高位是符号位:0表示正数,1表示负数 正数的原码,反码,补码都一样 负数的反码=它的原码符号位不变,其它位取反 负数的补码=它的反码+1 0 ...

  8. Azure Terraform(十)利用 Azure DevOps 的条件语句选择发布环境

    一,引言 之前我讲过的所有的案例中,都是将整个Azure Resource 部署到同一个订阅下,没有做到灵活的在 Azure Pipeline 在运行前选择需要部署的环境.在实际的项目开发中,我们也会 ...

  9. AOP-底层原理

    AOP(底层原理) 1,AOP底层使用动态代理 (1)有两种情况动态代理 第一种 有接口情况,使用JDK动态代理 *创建接口实现类代理对象,增强类的方法 第二种 无接口情况,使用CGLIB动态代理 * ...

  10. 事件驱动架构在 vivo 内容平台的实践

    一.什么是事件驱动架构 当下,随着微服务的兴起,容器化技术的发展,以及云原生.serverless 概念的普及,事件驱动再次引起业界的广泛关注. 所谓事件驱动的架构,也就是使用事件来实现跨多个服务的业 ...