仿QQ和仿网易新闻侧滑功能代码初识

先来个比较简单的例子

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"];
}







  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值