先来个比较简单的例子
1.仿QQ侧滑功能
//非常简便的方法初始化 主要的初始化代码如下
LeftViewController * left = [[LeftViewController alloc]init];
MainViewController * main = [[MainViewController alloc]init];
RightViewController * right = [[RightViewController alloc]init];
WWSideslipViewController * slide = [[WWSideslipViewController alloc]initWithLeftView:left andMainView:main andRightView:right andBackgroundImage:[UIImage imageNamed:@"bg.png"]];
首先创建一个继承于UIViewController的类WWSideslipViewController,主要实现的功能都放在这里面,在WWSideslipViewController.h中做的操作如下
<span style="font-family: Arial, Helvetica, sans-serif;">@interface WWSideslipViewController : UIViewController{</span>
@private
UIViewController * leftControl;
UIViewController * mainControl;
UIViewController * righControl;
UIImageView * imgBackground;
CGFloat scalef;
}
//滑动速度系数-建议在0.5-1之间。默认为0.5
@property (assign,nonatomic) CGFloat speedf;
//是否允许点击视图恢复视图位置。默认为yes
@property (strong) UITapGestureRecognizer *sideslipTapGes;
//初始化 instancetype(翻译:实例类型,其实这里用id类型就可以)
-(instancetype)initWithLeftView:(UIViewController *)LeftView
andMainView:(UIViewController *)MainView
andRightView:(UIViewController *)RighView
andBackgroundImage:(UIImage *)image;
//恢复位置
-(void)showMainView;
//显示左视图
-(void)showLeftView;
//显示右视图
-(void)showRighView;
在WWSideslipViewController.m中主要实现如下代码
#import "WWSideslipViewController.h"
@interface WWSideslipViewController ()
@end
@implementation WWSideslipViewController
//初始化方法
-(instancetype)initWithLeftView:(UIViewController *)LeftView
andMainView:(UIViewController *)MainView
andRightView:(UIViewController *)RighView
andBackgroundImage:(UIImage *)image;
{
if(self){
speedf = 0.5;
leftControl = LeftView;
mainControl = MainView;
righControl = RighView;
UIImageView * imgview = [[UIImageView alloc]initWithFrame:[UIScreen mainScreen].bounds];
[imgview setImage:image];
[self.view addSubview:imgview];
//滑动手势 UIPanGestureRecognizer是移动手势 摇动或者拖拽
UIPanGestureRecognizer * pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(handlePan:)];
[mainControl.view addGestureRecognizer:pan];
//单击手势 sideslipTapGes是UITapGestureRecognizer对象
sideslipTapGes= [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handeTap:)];
[sideslipTapGes setNumberOfTapsRequired:1];
[mainControl.view addGestureRecognizer:sideslipTapGes];
//刚开始让左右两个试图控制器全隐藏
leftControl.view.hidden = YES;
righControl.view.hidden = YES;
[self.view addSubview:leftControl.view];
[self.view addSubview:righControl.view];
[self.view addSubview:mainControl.view];
}
return self;
}
#pragma mark - 滑动手势
//滑动手势
- (void) handlePan: (UIPanGestureRecognizer *)rec{
//在指定的父视图坐标系统中,返回一个明确的新的当前坐标位置
//该方法返回在横坐标上、纵坐标上拖动了多少像素
CGPoint point = [rec translationInView:self.view];
/*
还有一个比较常用的方法
- (CGPoint)velocityInView:(UIView *)view方法解释如下:
在指定坐标系统中pan gesture拖动的速度
返回参数:返回这种速度
简单的理解就是
你拖动这个图片的时候肯定有个速度,因此返回值就是你拖动时X和Y轴上的速度,速度是矢量,有方向。
*/
//scalef为定义的一个全局的偏移量
scalef = (point.x*speedf+scalef);
NSLog(@"point.x:%f",point.x);
NSLog(@"scalef:%f",scalef);
//根据视图位置判断是左滑还是右边滑动
//rec.view表示添加滑动手势的那个视图
//这是向右滑 point.x*speedf
if (rec.view.frame.origin.x>=0){
rec.view.center = CGPointMake(rec.view.center.x + point.x*speedf ,rec.view.center.y);
//被滑动的那个视图缩小
rec.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,1-scalef/1000,1-scalef/1000);
//因为拖动起来一直是在递增,所以每次都要用setTranslation:方法将每次触摸都设置为0位置,这样才不至于不受控制般滑动出视图。
[rec setTranslation:CGPointMake(0, 0) inView:self.view];
//右边视图隐藏
righControl.view.hidden = YES;
//让左边试图显现
leftControl.view.hidden = NO;
}
//左滑
else
{
rec.view.center = CGPointMake(rec.view.center.x + point.x*speedf,rec.view.center.y);
//因为向左滑scalef是负数,所以要加上1才不会大于1
rec.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,1+scalef/1000,1+scalef/1000);
[rec setTranslation:CGPointMake(0, 0) inView:self.view];
righControl.view.hidden = NO;
leftControl.view.hidden = YES;
}
//手势结束后修正位置
if (rec.state == UIGestureRecognizerStateEnded) {
if (scalef>140*speedf){
[self showLeftView];
}
else if (scalef<-140*speedf) {
[self showRighView]; }
else
{
[self showMainView];
scalef = 0;
}
}
}
#pragma mark - 单击手势
-(void)handeTap:(UITapGestureRecognizer *)tap{
if (tap.state == UIGestureRecognizerStateEnded) {
[UIView beginAnimations:nil context:nil];
//恢复到原来的大小比例
tap.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,1.0,1.0);
tap.view.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2,[UIScreen mainScreen].bounds.size.height/2);
[UIView commitAnimations];
scalef = 0;
}
}
#pragma mark - 修改视图位置
//恢复位置
-(void)showMainView{
[UIView beginAnimations:nil context:nil];
mainControl.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,1.0,1.0);
mainControl.view.center = CGPointMake([UIScreen mainScreen].bounds.size.width/2,[UIScreen mainScreen].bounds.size.height/2);
[UIView commitAnimations];
}
//显示左视图
-(void)showLeftView{
[UIView beginAnimations:nil context:nil];
//这里的0.8 340都是设置主控制器停止后的位置及大小 可以改变这个个值的大小从而得到自己想要的结果
mainControl.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,0.8,0.8);
mainControl.view.center = CGPointMake(340,[UIScreen mainScreen].bounds.size.height/2);
[UIView commitAnimations];
}
//显示右视图
-(void)showRighView{
[UIView beginAnimations:nil context:nil];
mainControl.view.transform = CGAffineTransformScale(CGAffineTransformIdentity,0.8,0.8);
mainControl.view.center = CGPointMake(-60,[UIScreen mainScreen].bounds.size.height/2);
[UIView commitAnimations];
}
#warning 为了界面美观,所以隐藏了状态栏。如果需要显示则去掉此代码
- (BOOL)prefersStatusBarHidden
{
return YES; //返回NO表示要显示,返回YES将hiden
}
@end
在APPDelegate.m中
LeftViewController * left = [[LeftViewController alloc]init];
MainViewController * main = [[MainViewController alloc]init];
RightViewController * right = [[RightViewController alloc]init];
WWSideslipViewController * slide = [[WWSideslipViewController alloc]initWithLeftView:left andMainView:main andRightView:right andBackgroundImage:[UIImage imageNamed:@"bg.png"]];
//滑动速度系数
[slide setSpeedf:0.5];
//点击视图是是否恢复位置
slide.sideslipTapGes.enabled = YES;
[self.window setRootViewController:slide];
2.仿网易新闻的侧滑功能
先来看一下效果图
这个实现的侧滑功能是比较复杂的,设计很多类和方法,主要的实现模块和代码如下
1).创建方式和上面的代码雷同,都是先创建一个继承于UIViewController的类QHSliderViewController,再创建三个视图控制器,在AppDelegate.m的实现代码为:
LeftViewController *leftVC = [[LeftViewController alloc] init];
[leftVC.view setFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width*0.75, [UIScreen mainScreen].bounds.size.height)];
[QHSliderViewController sharedSliderController].LeftVC = leftVC;
RightViewController *rightVC = [[RightViewController alloc] init];
[QHSliderViewController sharedSliderController].RightVC = rightVC;
[QHSliderViewController sharedSliderController].finishShowRight = ^()
{
[rightVC headPhotoAnimation];
};
[QHSliderViewController sharedSliderController].MainVC = [[MainAppViewController alloc] init];
UINavigationController *naviC = [[UINavigationController alloc] initWithRootViewController:[QHSliderViewController sharedSliderController]];
self.window.rootViewController = naviC;
2).在QHSliderViewController.m中首先定义了三个UIView视图,分别为主视图,左视图,右视图,并对其进行初始化,然后设置左右两个子控制器,在这里写了一个- (void)initChildControllers:(UIViewController*)leftVC rightVC:(UIViewController*)rightVC方法,实现方法如下:
- (void)initChildControllers:(UIViewController*)leftVC rightVC:(UIViewController*)rightVC
{
if (_canShowRight&&rightVC!=nil) {
//如果可以显示右边并且右视图控制器存在,就将右视图控制器作为QHSliderViewController的子控制器
[self addChildViewController:rightVC];
rightVC.view.frame=CGRectMake(0, 0, rightVC.view.frame.size.width, rightVC.view.frame.size.height);
[_rightSideView addSubview:rightVC.view];
}
if (_canShowLeft&&leftVC!=nil) {
[self addChildViewController:leftVC];
_nDurationLeft = self.view.frame.size.width - leftVC.view.frame.size.width;
NSLog(@"self.view.frame.size.width:%f",self.view.frame.size.width);//320
NSLog(@"leftVC.view.frame.size.width:%f",leftVC.view.frame.size.width);//320*0.75
leftVC.view.frame=CGRectMake(_nDurationLeft, 0, leftVC.view.frame.size.width, leftVC.view.frame.size.height);
[_leftSideView addSubview:leftVC.view];
}
}
3).在这里比较重要的一个方法是主视图切换不同界面的方法- (void)showContentControllerWithModel:(NSString *)className,这个方法可以显示QHSliderViewController中作为主界面的那个页面上的信息
//显示控制器和模型内容 主视图切换不同界面的方法
- (void)showContentControllerWithModel:(NSString *)className
{
// [self closeSideBar];
UIViewController *controller = _controllersDict[className];
//如果字典里没有controller这个对象
if (!controller)
{
//就通过类名生成一个新的视图控制器类
Class c = NSClassFromString(className);
#if __has_feature(objc_arc)//如果使用的是arc的话
controller = [[c alloc] init];
#else //如果不使用arc
controller = [[[c alloc] init] autorelease];
#endif
//将这个类再添加到装有类的一个字典里
[_controllersDict setObject:controller forKey:className];
}
//如果主视图上有子视图的话就移除,将新生成的视图控制器的view给主视图
if (_mainContentView.subviews.count > 0)
{
UIView *view = [_mainContentView.subviews firstObject];
[view removeFromSuperview];
}
controller.view.frame = _mainContentView.frame;
[_mainContentView addSubview:controller.view];
//新生成的那个视图控制器赋给显示在QHSliderViewController上的那个主视图
self.MainVC=controller;
}
4).显示左边视图控制器的方法 右视图控制器的显示只是坐标稍微调整一下即可
- (void)showLeftViewController
{
if (showingLeft) {
[self closeSideBar];
return;
}
if (!_canShowLeft||_LeftVC==nil) {
return;
}
showingLeft = YES;
[self.view bringSubviewToFront:_leftSideView];
// float durationTime = (-_leftSideView.frame.origin.x)/(_MainVC.view.frame.size.width);
[self configureViewBlurWith:0 scale:0.8];
[UIView animateWithDuration:COMMON_DURATION_TIME animations:^
}
[self configureViewBlurWith:_MainVC.view.frame.size.width scale:0.8];//毛玻璃效果
[_leftSideView setFrame:CGRectMake(-_nDurationLeft, _leftSideView.frame.origin.y, _leftSideView.frame.size.width, _leftSideView.frame.size.height)];
} completion:^(BOOL finished)
{
_leftSideView.userInteractionEnabled = YES;
_tapGestureRec.enabled = YES;
}];
}
5).根据点击的状态来实现拖拽过程中各个视图控制器的转变
<pre name="code" class="objc">- (void)moveViewWithGesture:(UIPanGestureRecognizer *)panGes
{
static CGFloat startX;
static CGFloat lastX;
static CGFloat durationX;//durationX持续时间
CGPoint touchPoint = [panGes locationInView:[[UIApplication sharedApplication] keyWindow]];
if (panGes.state == UIGestureRecognizerStateBegan)
{
startX = touchPoint.x;
lastX = touchPoint.x;
}
if (panGes.state == UIGestureRecognizerStateChanged)
{
CGFloat currentX = touchPoint.x;
durationX = currentX - lastX;
lastX = currentX;
if (durationX > 0)//说明是向右拖拽,显示左视图
{
if(!showingLeft && !showingRight)
{
showingLeft = YES;
[self.view bringSubviewToFront:_leftSideView];
}
}else//(durationX<0) //说明是向左拖拽,显示右视图
{
if(!showingRight && !showingLeft)
{
showingRight = YES;
[self.view bringSubviewToFront:_rightSideView];
}
}
if (showingLeft)//向右拖拽并且已经显示左视图
{
if (_leftSideView.frame.origin.x >= -_nDurationLeft && durationX > 0)
{
return;
}
if (!_canShowLeft||_LeftVC==nil) {
return;
}
//设置毛玻璃状态
[self configureViewBlurWith:currentX scale:0.8];
float x = durationX + _leftSideView.frame.origin.x;
NSLog(@"x:%f",x);//从-320一直增加到-80
//设置左视图随着拖拽慢慢显现出来
[_leftSideView setFrame:CGRectMake(x, _leftSideView.frame.origin.y, _leftSideView.frame.size.width, _leftSideView.frame.size.height)];
}
else //durationX < 0 //向左拖拽显示右视图
{
if (!_canShowRight||_RightVC==nil) {
return;
}
[self configureViewBlurWith:(self.view.frame.size.width - currentX) scale:0.8];
float x = durationX + _rightSideView.frame.origin.x;
NSLog(@"x:%f",x);//x逐渐减小 从320一直减小到0
[_rightSideView setFrame:CGRectMake(x, _rightSideView.frame.origin.y, _rightSideView.frame.size.width, _rightSideView.frame.size.height)];
}
}
else if (panGes.state == UIGestureRecognizerStateEnded)
{
if (showingLeft)
{
// NSLog(@"_leftSideView.frame.origin.x + _leftSideView.frame.size.width:%f",_leftSideView.frame.origin.x + _leftSideView.frame.size.width);//244.5
// NSLog(@"_leftSideView.frame.size.width:%f",_leftSideView.frame.size.width);//320
// NSLog(@"_leftSideView.frame.origin.x :%f",_leftSideView.frame.origin.x );//-80
// NSLog(@"_MainVC.view.frame.size.width :%f",_MainVC.view.frame.size.width );//320
if (!_canShowLeft||_LeftVC==nil) {
return;
}
//说明完成向右拖拽左视图 ,左视图控制器显现出来
if ((_leftSideView.frame.origin.x + _leftSideView.frame.size.width) > (_leftSideView.frame.size.width - _nDurationLeft)/2)
{
float durationTime = (-_leftSideView.frame.origin.x)/(_MainVC.view.frame.size.width);// 320/320
[UIView animateWithDuration:durationTime animations:^
{
[self configureViewBlurWith:_MainVC.view.frame.size.width scale:0.8];
[_leftSideView setFrame:CGRectMake(-_nDurationLeft, _leftSideView.frame.origin.y, _leftSideView.frame.size.width, _leftSideView.frame.size.height)];
} completion:^(BOOL finished)
{
_leftSideView.userInteractionEnabled = YES;
_tapGestureRec.enabled = YES;
}];
}else //说明完成向左拖拽左视图 ,左视图控制器隐藏
{
float durationTime = 1 - (-_leftSideView.frame.origin.x)/(_MainVC.view.frame.size.width);
[UIView animateWithDuration:durationTime animations:^
{
[self configureViewBlurWith:0 scale:0.8];
[_leftSideView setFrame:CGRectMake(-_leftSideView.frame.size.width, _leftSideView.frame.origin.y, _leftSideView.frame.size.width, _leftSideView.frame.size.height)];
} completion:^(BOOL finished)
{
[self.view sendSubviewToBack:_leftSideView];
showingLeft = NO;
showingRight = NO;
_tapGestureRec.enabled = NO;
[self removeconfigureViewBlur];
}];
}
return;
}
if (showingRight)
{
if (!_canShowRight||_RightVC==nil) {
return;
}
//如果向左拖拽,右视图还没被完全显现出来
if (_rightSideView.frame.origin.x < _MainVC.view.frame.size.width/2)
{
float durationTime = (_rightSideView.frame.origin.x)/(_MainVC.view.frame.size.width);
[UIView animateWithDuration:durationTime animations:^
{
[self configureViewBlurWith:_MainVC.view.frame.size.width scale:1];
[_rightSideView setFrame:CGRectMake(0, _rightSideView.frame.origin.y, _rightSideView.frame.size.width, _rightSideView.frame.size.height)];
} completion:^(BOOL finished)
{
_rightSideView.userInteractionEnabled = YES;
_tapGestureRec.enabled = YES;
if (self.finishShowRight != nil)
{
self.finishShowRight();
}
}];
}else //如果是向右拖拽,想要隐藏右视图控制器
{
float durationTime = 1 - (_rightSideView.frame.origin.x)/(_MainVC.view.frame.size.width);
[UIView animateWithDuration:durationTime animations:^
{
[self configureViewBlurWith:0 scale:1];
[_rightSideView setFrame:CGRectMake(_MainVC.view.frame.size.width, _rightSideView.frame.origin.y, _rightSideView.frame.size.width, _rightSideView.frame.size.height)];
} completion:^(BOOL finished)
{
[self.view sendSubviewToBack:_rightSideView];
showingLeft = NO;
showingRight = NO;
_tapGestureRec.enabled = NO;
[self removeconfigureViewBlur];
}];
}
}
}
}
以上就是侧滑的主要的代码,其中奥秘还须自己细细品味
3.在这个Demo中其实还有许多细节可值得研究
1).现在,xcode对于iOS4.0以上的系统,支持用block来遍历元素了。用block来遍历字典或者数组可以简化代码的编写,建议大家都使用上这个新特性。
[_arDataenumerateObjectsUsingBlock:^(NSString *obj,NSUInteger idx,BOOL *stop)
先看一下用这个方法可以实现的效果图
具体的使用方法如下:
//首先定义一个数组
_arData = @[@"新闻", @"订阅", @"图片", @"视频", @"跟帖", @"电台"];
//因为block块中要用到h和y这几个变量,要对其进行修改,必须将其设置成__block形式
__block float h = self.view.frame.size.height*0.7/[_arData count];
__block float y = 0.15*self.view.frame.size.height;
//调用回调的方法
[_arData enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop)
{
//listV为创建整个条目的父视图,即把那些分类都放在这个view上
UIView *listV = [[UIView alloc] initWithFrame:CGRectMake(0, y, self.view.frame.size.width-80, h)];
[listV setBackgroundColor:[UIColor clearColor]];
//l为每一个放置分类的label
UILabel *l = [[UILabel alloc] initWithFrame:CGRectMake(60, 0, listV.frame.size.width - 60, listV.frame.size.height)];
[l setFont:[UIFont systemFontOfSize:20]];
[l setTextColor:[UIColor whiteColor]];
[l setBackgroundColor:[UIColor clearColor]];
//obj为数组里的每一个元素,依次提取出来放在label上
[l setText:obj];
[listV addSubview:l];
[self.view addSubview:listV];
y += h;
}];
2).实现动画的反转
在右视图控制器内实现上面两幅图的边旋转边替换出现
//实现2个图片的交替出现
- (void)headPhotoAnimation
{
[self rotate360WithDuration:2.0 repeatCount:1];
_headImageView.animationDuration = 2.0;
_headImageView.animationImages = [NSArray arrayWithObjects:
[UIImage imageNamed:@"head1.jpg"],
[UIImage imageNamed:@"head2.jpg"],
[UIImage imageNamed:@"head2.jpg"],
[UIImage imageNamed:@"head2.jpg"],
[UIImage imageNamed:@"head2.jpg"],
[UIImage imageNamed:@"head1.jpg"],
nil];
_headImageView.animationRepeatCount = 1;
[_headImageView startAnimating];
}
//使图片旋转360度的方法
- (void)rotate360WithDuration:(CGFloat)aDuration repeatCount:(CGFloat)aRepeatCount
{
//关键帧动画
CAKeyframeAnimation *theAnimation = [CAKeyframeAnimation animation];
theAnimation.values = [NSArray arrayWithObjects:
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(0, 0,1,0)],
//沿Y轴旋转旋转180度
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0,1,0)],
//沿Y轴旋转旋转180度
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(M_PI, 0,1,0)],
//沿Y轴旋转旋转360度
[NSValue valueWithCATransform3D:CATransform3DMakeRotation(2*M_PI, 0,1,0)],
nil];
//旋转效果累计,先转180度,接着再旋转180度,从而实现360旋转
theAnimation.cumulative = YES;
theAnimation.duration = aDuration;
theAnimation.repeatCount = aRepeatCount;
//完成后删除
theAnimation.removedOnCompletion = YES;
[_headImageView.layer addAnimation:theAnimation forKey:@"transform"];
}