iOS利用余弦函数实现卡片浏览工具

本文实例为大家分享了iOS利用余弦函数实现卡片浏览工具的具体代码,供大家参考,具体内容如下

一、实现效果

通过拖拽屏幕实现卡片移动,左右两侧的卡片随着拖动变小,中间的变大。效果如下:

二、原理说明

1、上面的动画效果是根据余弦函数的曲线特性实现的,先看一下函数曲线y=cos(x),在区间-π/2 到 π/2的范围内,y的值在x的0的是后是最大的,左右则越来越小。

2、可以将被滚动的卡片的高度按照0.0~1.0的比例放大缩小,效果如下:

3、放置到手机屏幕上的效果如下:

三、代码

封装每个卡片为Card.h

卡片显示在CardSwitchView.h上

代码思路是假设控件的中心为原点,中轴线为x轴和y轴,当卡片的中心为距离y轴越近时,卡片长度缩短的比例越趋近1.0,当卡片中线距离y轴越远时,卡片长度缩短的比例越趋近0;

如下图所示假设方块从位置1到位置2向左移动了长度a(写代码时需要做角度和长度的转换),那么在曲线上b的值为cos(a),假设b=0.8,那么就在位置2的时候把高度缩短为原来的0.8倍,以此类推越趋近于控件中轴线的位置卡片越长。(这里角度和长度的转换倍数依情况而定)

//
// CardSwitchView.m
// CardSwitchDemo
//
// Created by Apple on 2016/11/9.
// Copyright © 2016年 Apple. All rights reserved.
//

#import "CardSwitchView.h"
#import "Card.h"

//播放器界面的的宽度所占的比例
static float viewScale = 0.70f;

@interface CardSwitchView ()<UIScrollViewDelegate>
{
 //用于切换的ScrollView
 UIScrollView *_scrollView;
 //用于保存各个视图
 NSMutableArray *_cards;
 //滚动之前的位置
 CGFloat _startPointX;
 //滚动之后的位置
 CGFloat _endPointX;
 //需要居中显示的index
 NSInteger _currentIndex;
}
@end

@implementation CardSwitchView

-(instancetype)initWithFrame:(CGRect)frame
{
 if (self = [super initWithFrame:frame]) {
 [self buildLayout];
 }
 return self;
}

-(void)buildLayout
{
 //初始化ScrollView
 _scrollView = [[UIScrollView alloc] initWithFrame:self.bounds];
 _scrollView.delegate = self;
 _scrollView.showsHorizontalScrollIndicator = false;
 [self addSubview:_scrollView];

 //初始化其他参数
 _cards = [[NSMutableArray alloc] init];
 _currentIndex = 0;
}

#pragma mark -
#pragma mark 视图Frame配置

//卡片宽度
-(float)cardWidth
{
 return viewScale*self.bounds.size.width;
}

//卡片间隔
-(float)margin
{
 return (self.bounds.size.width - [self cardWidth])/4;
}
//卡片起始位置
-(float)startX
{
 return (self.bounds.size.width - [self cardWidth])/2.0f;
}

#pragma mark -
#pragma mark 配置轮播图片
-(void)setCardNumber:(NSInteger)cardNumber
{
 _cardNumber = cardNumber;
 //初始化各个播放器位置
 for (NSInteger i = 0; i<cardNumber; i++ ) {
 //第一步 在ScrollView上添加卡片
 float viewX = [self startX] + i*([self cardWidth] + [self margin]);
 Card* card = [[Card alloc] initWithFrame:CGRectMake(viewX, 0, [self cardWidth], self.bounds.size.height)];
 card.layer.borderWidth = 1.0f;
 card.index = i;
 [_scrollView addSubview:card];
 [_cards addObject:card];
 [_scrollView setContentSize:CGSizeMake(card.frame.origin.x + [self cardWidth] + 2*[self margin], 0)];
 }
 //更新卡片的大小
 [self updateCardTransform];
}

#pragma mark -
#pragma mark ScrollView代理方法
//开始拖动时保存起始位置
-(void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
 _startPointX = scrollView.contentOffset.x;
}

//当ScrollView拖动时 变换每个view的大小,并保证居中屏幕的view高度最高
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
 [self updateCardTransform];
}

//滚动结束,自动回弹到居中卡片
-(void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
{
 //滚动到视图中间位置
 dispatch_async(dispatch_get_main_queue(), ^{
 [self scrollToCurrentCard];
 });
}

//卡片自动居中
-(void)scrollToCurrentCard
{
 _endPointX = _scrollView.contentOffset.x;
 //设置滚动最小生效范围,滚动超过scrollMiniDistance 即视为有切换卡片的意向
 float scrollMiniDistance = self.bounds.size.width/30.0f;
 if (_startPointX - _endPointX > scrollMiniDistance) {
 NSLog(@"向右滑动屏幕");
 if (_currentIndex != 0) {
  _currentIndex -= 1;
 }
 }else if (_endPointX - _startPointX > scrollMiniDistance)
 {
 NSLog(@"向左滑动屏幕");
 if (_currentIndex != _cards.count - 1) {
  _currentIndex += 1;
 }
 }
 float viewX = [self startX] + _currentIndex*([self cardWidth] + [self margin]);
 float needX = viewX - [self startX];
 [_scrollView setContentOffset:CGPointMake(needX, 0) animated:true];
}

//更新每个卡片的大小
-(void)updateCardTransform
{
 for (Card *card in _cards) {
 //获取卡片所在index
 //获取ScrollView滚动的位置
 CGFloat scrollOffset = _scrollView.contentOffset.x;
 //获取卡片中间位置滚动的相对位置
 CGFloat cardCenterX = card.center.x - scrollOffset;
 //获取卡片中间位置和父视图中间位置的间距,目标是间距越大卡片越短
 CGFloat apartLength = fabs(self.bounds.size.width/2.0f - cardCenterX);
 //移动的距离和屏幕宽度的的比例
 CGFloat apartScale = apartLength/self.bounds.size.width;
 //把卡片移动范围固定到 -π/4到 +π/4这一个范围内
 CGFloat scale = fabs(cos(apartScale * M_PI/4));
 //设置卡片的缩放
 card.transform = CGAffineTransformMakeScale(1.0, scale);
 }
}

@end

Demo下载

GitHub项目

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • iOS自定义View实现卡片滑动

    本文实例为大家分享了iOS自定义View实现卡片滑动效果的具体代码,供大家参考,具体内容如下 说明 控件基于UIView封装完成,采用UIPanGestureRecognizer监听自身的触摸事件,以此处理各种滑动动画操作. 内容之间可以循环切换,采用类似tableView加载机制,达到复用效果 效果 代码实现 #import <UIKit/UIKit.h> @class SMSwipeView; @protocol SMSwipeDelegate <NSObject> @requ

  • iOS UICollectionView实现卡片效果

    现在使用卡片效果的app很多,之前公司让实现一种卡片效果,就写了一篇关于实现卡片的文章.文章最后附有demo 实现上我选择了使用UICollectionView :用UICollectionViewFlowLayout来定制样式:下面看看具体实现 具体实现 1.创建UICollectionView - (void)createCollectionView { CGFloat pading = 0 * SCREEN_WIDTH/375; LHLeftCollocationView * layout

  • iOS实现卡片堆叠效果

    本文实例为大家分享了iOS实现卡片堆叠效果的具体代码,供大家参考,具体内容如下 如图,这就是最终效果. 去年安卓5.0发布的时候,当我看到安卓全新的Material Design设计语言后,真的是喜欢的不得了,这种设计语言不同于偏平式设计以及卡片式设计.简约,自然.直到15年初,偶然看到CM团队已经发布了好多基于安卓5.0的ROM,恰巧有我手机对应的版本,便迫不及待的刷了固件,体验了一把. 不得不说的是,安卓的这个版本简直历史性变革,更加流畅,好用,而且在开发者模式下,发现这个版本移除了Dalv

  • iOS实现3D卡片式轮播效果

    本文实例为大家分享了iOS实现3D卡片式轮播效果的具体代码,供大家参考,具体内容如下 效果: 参考UITableView的UITableViewDataSource和UITableViewDelegate两个方法实现:支持五险轮播,可以加载本地图片,也可以加载网络图片,可以根据自己的需求自定义 Demo地址 UITableViewDelegate /** * 当前显示cell的Size(中间页显示大小) * * @param flowView <#flowView description#>

  • iOS实现卡片式滚动效果 iOS实现电影选片效果

    本文实例为大家分享了iOS实现卡片式滚动效果的具体代码,供大家参考,具体内容如下 先来张效果图吧: 直接上源码了就(工作比较忙,就不一一解释了,有问题可以Q一同讨论,793136807): CardScrollView.h #import <UIKit/UIKit.h> @interface CardView : UIView @property (nonatomic, assign) CGFloat zoomRate; @property (nonatomic, strong) NSStri

  • iOS利用余弦函数实现卡片浏览工具

    本文实例为大家分享了iOS利用余弦函数实现卡片浏览工具的具体代码,供大家参考,具体内容如下 一.实现效果 通过拖拽屏幕实现卡片移动,左右两侧的卡片随着拖动变小,中间的变大.效果如下: 二.原理说明 1.上面的动画效果是根据余弦函数的曲线特性实现的,先看一下函数曲线y=cos(x),在区间-π/2 到 π/2的范围内,y的值在x的0的是后是最大的,左右则越来越小. 2.可以将被滚动的卡片的高度按照0.0~1.0的比例放大缩小,效果如下: 3.放置到手机屏幕上的效果如下: 三.代码 封装每个卡片为C

  • Python绘制正余弦函数图像的方法

    今天打算通过绘制正弦和余弦函数,从默认的设置开始,一步一步地调整改进,让它变得好看,变成我们初高中学习过的图象那样.通过这个过程来学习如何进行对图表的一些元素的进行调整. 01. 简单绘图 matplotlib有一套允许定制各种属性的默认设置.你可以几乎控制matplotlib中的每一个默认属性:图像大小,每英寸点数,线宽,色彩和样式,子图(axes),坐标轴和网格属性,文字和字体属性,等等. 安装 pip install matplotlib 虽然matplotlib的默认设置在大多数情况下相

  • 利用Python函数实现一个万历表完整示例

    前言 大家可以根据格式化打印字符去调一下最后的输出,不过有中文好像不好调整,可以换成星期的单词,这样应该会好一点,format()函数可以用来格式化打印字符,format()可以使用字符串去调用,也可以独自使用. 可以点进格式化打印字符了解一下哦 示例代码 # 判断是否闰年 def isleap(year): return year % 4 == 0 and year % 100 != 0 or year % 400 == 0 # 判断月的天数 def month_days(year,month

  • R语言利用plot()函数画图的基本用法

    plot()函数在R语言画图中位置十分重要,现在就对其具体用法做一个总结. 基本用法: plot(x=x轴数据,y=y轴数据,main="标题",sub="子标题",type="线型",xlab="x轴名称",ylab="y轴名称",xlim = c(x轴范围,x轴范围),ylim = c(y轴范围,y轴范围)) 示例代码为: plot(c(1:6),c(1:6),main="test"

  • 利用.NET 开发服务器 应用管理工具

    目录 一.背景 1.Anno.Deploy 二.使用方法 1.和Anno集成使用 2.单独使用 3.annoDeploy 配置文件 4.annoDeploy 配置一个守护程序 一.背景 1.Anno.Deploy Anno.Deploy可以和 Anno集成使用,用于部署新的服务.启动服务.停止服务.清理服务.也可以单独使用,用于守护程序. 二.使用方法 1.和Anno集成使用 2.单独使用 annoDeploy status 查看程序状态 annoDeploy start [workingNam

  • 利用Python实现Picgo图床工具

    目录 一.PyPicGo 1.安装 2.配置 3.使用 4.支持的图床 5.支持的插件 6.Uploader上传器 7.插件系统 8.开发 一.PyPicGo PyPicGo 是一款图床工具,是PicGo是Python版实现,并支持各种插件自定义插件,目前PyPicGo自带了gitee.github.SM.MS和七牛云图传,以及rename.notify和typora等插件,并支持从pypi中下载其他插件和Uploader 源码地址: [gitee]https://gitee.com/Range

  • Python利用format函数实现对齐打印(左对齐、右对齐与居中对齐)

    目录 forma格式化的用法 用format函数实现对齐打印 居中对齐示例 右对齐示例 左对齐示例 总结 forma格式化的用法 format函数可以接受不限个参数,位置可以不按顺序. 基本语法是通过{ }和:来代替c语言的%. >>> a="名字是:{0},年龄是:{1}" >>> a.format("煮雨",18) '名字是:煮雨,年龄是:18' {0},{1}代表的占位符,数字占位符要注意顺序. >>> c

  • 利用Go语言实现流量回放工具的示例代码

    目录 前言 goreplay介绍与安装 使用示例 流量放大.缩小 流量写入到ElastichSearch goreplay基本实现原理 总结 前言 哈喽,大家好,我是asong. 今天给大家推荐一款使用Go语言编写的流量回放工具 -- goreplay:工作中你一定遇到过需要在服务器上抓包的场景,有了这个工具就可以助你一臂之力,goreplay的功能十分强大,支持流量的放大.缩小,并且集成了ElasticSearch,将流量存入ES进行实时分析: 废话不多,我们接下来来看一看这个工具: gore

  • vue中如何利用js函数截取时间的年月日时分秒

    目录 前言 解决方法: 1. 使用自定义函数格式化时间数据: 2. 利用substring函数截取: 3. 利用left函数截取 4. 利用right函数截取 总结 前言 在vue中,从后台获取数据,如果不是sql语句(类似:select convert(char(10),GetDate(),120) as Date)获取的数据,在前台就会出现如下情况: 解决方法: 1. 使用自定义函数格式化时间数据: (1)年月日: var timearr = time.replace(" ", &

  • Rust利用tauri制作个效率小工具

    目录 先看效果 要解决的问题 唤起方式 菜单出现的位置 最后 日常使用电脑中经常会用到一个quicke工具中的轮盘菜单工具. 但quicke免费版很多功能不支持,且它的触发逻辑用的不舒服,经常误触. 本着靠人不让靠自己,自己动手丰衣足食的理念,用tauri撸一个小工具. 先看效果 要解决的问题 唤起方式 因为要通过鼠标进行交互,所以必然会影响系统默认的鼠标行为. 为了减少交互冲突,选择长按右键唤起菜单. 就要解决如下问题: 1.默认情况要阻止右键鼠标的事件传递给操作系统 2.当右键按住时间小于设

随机推荐