基本数据采集

经过体验,手机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. 『无为则无心』Python函数 — 29、Python变量和参数传递

    目录 1.Python的变量 (1)Python变量不能独立存在 (2)变量是内存中数据的引用 (3)注意点 2.了解变量的引用 3.Python的参数传递(重点) (1)示例 (2)结论 (3)总结 ...

  2. 探索 dotnet core 为何在 Windows7 系统需要补丁的原因

    在一些 Windows 7 系统上,根据 dotnet 官方文档,需要安装上 KB2533623 补丁,才能运行 dotnet core 或 .NET 5 等应用.尽管非所有的设备都需要安装此,但这也 ...

  3. hisql ORM 框架研究(国内第一个支持HANA的ORM框架)

    HiSql 操作说明文档 V1.0 下一代ORM框架 国内第一个支持HANA的ORM框架 hisql源码下载 git clone https://github.com/tansar/HiSql.git ...

  4. centos7-collabora-office(在线文档编辑)

    1.wget https://www.collaboraoffice.com/repos/CollaboraOnline/CODE-centos7/repodata/repomd.xml.key &a ...

  5. 华为云 Kubernetes 管理员实训 五 课后作业

    练习1 部署一个Deployment应用,使用secret普通卷,该应用启动依赖此secret. Deployment的名称为<hwcka-005-1-你的华为云id> 将所用命令.创建的 ...

  6. STM32寄存器深入分析

    可能很多刚开始学习STM32的小伙伴都有一个疑惑,创建项目时会需要很多头文件,导致学习过程中很难明白那些头文件的作用,虽然知道头文件都是对寄存器的封装,但是怎么封装的就不知道了.这里我以led灯为试验 ...

  7. Web开发之HTTP协议

    HTTP响应消息 一个HTTP响应代表服务器向客户端回送的数据. 一个完整的HTTP响应包括如下内容: 一个状态行.若干消息头.以及响应正文,其中的一些消息头和正文都是可选的,消息头和正文内容之间要用 ...

  8. ctfshow萌新 web1-7

    ctfshow萌新 web1 1.手动注入.需要绕过函数inval,要求id不能大于999且id=1000,所以用'1000'字符代替数字1000 2.找到?id=" "处有回显 ...

  9. JavaScript中数字常用方法

    数字常用方法 (1)返回0-1(不包含1)随机小数(random()) 格式:Math.random() var num=parseInt(Math.random()*11) //拿到0-10中的随机 ...

  10. golang gin框架中实现大文件的流式上传

    一般来说,通过c.Request.FormFile()获取文件的时候,所有内容都全部读到了内存.如果是个巨大的文件,则可能内存会爆掉:且,有的时候我们需要一边上传一边处理. 以下的代码实现了大文件流式 ...