美文网首页Swift编程@IT·互联网iOS Developer
swfit的QQ抽屉和移动按钮代替一成不变TabbarVC的架构

swfit的QQ抽屉和移动按钮代替一成不变TabbarVC的架构

作者: 七秒记忆的鱼儿 | 来源:发表于2016-08-11 10:20 被阅读312次

    现在大家使用的结构基本都是NAV+TAB的一个模式,又是也会在里面增加一个抽屉的模式。大家可能都会认为抽屉结构谁不会写的啊,我在网上也看到一些Demo,上面大部分的都是在首页的按钮有个点击侧滑,或者再试在首页上面有侧滑而已,却不能在tabbar上面任何的一个控制器下面进行策划,而QQ的确实可以的,自己也很苦恼想了很久,最后想到我可不可以将策划加到一个tabbar注控制器上面,但是侧滑的时候,侧滑的控制器我该加到什么地方呢?最后想到了一个方法:

    先看下我们最终的效果吧

    Untitled.gif

    1.我可以创建一个主的VC(AVC)里面包含两个控制器,一个是tabbar,一个是侧滑控制器,将手势添加到注控制器上面就可以完美解决了

    //在主控制器里面创建个方法,来包含两个控制器
    func   initCreat(mainVC_:UIViewController?,leftVC_:UIViewController?)->UIViewController
    {
        mainVC = mainVC_;
        leftVC = leftVC_;
        leftVC_?.view.frame = CGRectMake(-0.4*kPalyhighWidth, 0, 0.8*kPalyhighWidth, kPalyhighHeight);
        view .addSubview((leftVC_?.view)!);
        leftVC_?.view.hidden = true;
        mainVC_?.view.frame = self.view.bounds;
        self.view .addSubview((mainVC_?.view)!);
    
        return self;
    }
    

    2.0 将手势添加到注控制器上面

    override func viewDidLoad() {
        super.viewDidLoad()
     //添加滑动手势
         pan =  UIPanGestureRecognizer(target: self
            , action: "pan:")
        view.addGestureRecognizer(pan!);
    }
    

    3.0这样我们就可以设置window的rootVC为我们的主控制器(AVC)

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        window = UIWindow(frame:UIScreen.mainScreen().bounds)
        window?.backgroundColor = UIColor.whiteColor();
        let mainVc = MainTabbarVC();
    //TODO: 更选你需要的结构 Rotat表示+号结构  Normal表示默认的tabbar结构
        mainVc.tabbarStyle = .Rotat;
        let leftVC:UIViewController = MyViewController();
        window?.rootViewController = MainVC().initCreat(mainVc, leftVC_: leftVC);
        window?.makeKeyAndVisible();
    
        return true   
    }
    

    4.0到此我们就可以实现在tabbarVC里面任何的控制器上面进行侧滑了,想侧滑出现什么的效果,我们就在侧滑调用的“pan:”方法去实现了。我实现的就是QQ的效果

    func pan(sender:UIPanGestureRecognizer) {

        if sender.state == .Began
        {
          leftVC?.view.hidden = false;
           point = sender.locationInView(self.view)
        }else if  sender.state == .Changed
        {
            changePoint = sender.locationInView(view);
            var width = changePoint.x - point.x;
           if openLeftVC == false
           {// 左视图还没显示出来
                if width <= 0 {
                    return ;
                }
                if width >= 0.8*kPalyhighWidth
                {// 判断是否越界
                    width = 0.8*kPalyhighWidth;
                }
                mainVC?.view.frame.origin.x = width
                leftVC?.view.center.x = width/2;
            
           }else{// 左视图显示出来了
                var width_ =  point.x - changePoint.x ;
                if width_ <= 0
                {
                    return;
                }
                if width_ >= 0.8*kPalyhighWidth
                {// 判断是否越界
                    width_ = 0.8*kPalyhighWidth;
                }
                mainVC?.view.frame.origin.x = 0.8*kPalyhighWidth - width_
                leftVC?.view.center.x = 0.4*kPalyhighWidth - width_/2;
            
                }
        } else if sender.state == .Ended
        {
            sender.setTranslation(CGPointZero, inView: view)
            //没有到达屏幕的一半
            if mainVC?.view.frame.origin.x < 0.5*kPalyhighWidth{
                openLeftVC = false;
    
                self.hiddenLeftView()
            }else {
                openLeftVC = true;
                self.showLeftView()
                
            }
        }
        
    }
    

    其中有两个隐藏和显示的方法

    //收起左视图         
    func hiddenLeftView(){
        
        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.leftVC?.view.frame.size.width = kPalyhighWidth;
            self.leftVC?.view.frame.origin.x = -0.4*kPalyhighWidth
            self.mainVC?.view.frame.origin.x = 0;
            }) { (_) -> Void in
                self.leftVC?.view.hidden = true;
               self.rightView.removeFromSuperview();
        }
    }
    //显示左视图
    func showLeftView() {
    
        UIView.animateWithDuration(0.5, animations: { () -> Void in
            self.leftVC?.view.frame.size.width = kPalyhighWidth*0.8;
            self.leftVC?.view.frame.origin.x = 0
            self.mainVC?.view.frame.origin.x = 0.8*kPalyhighWidth;
            }) { (_) -> Void in
                self.view.addSubview(self.rightView)
        }
    }
    
    11.gif

    现在我们来说下中间的加号按钮的实现方式

    1.0 按钮是可以任意拖动的,并且是根据你移动的位置来移动的,不用多说先上一个移动手势吧,我就自己自定义了一个button类,并在上面添加了移动手势

    init(frame: CGRect,addView:UIView) {
        super.init(frame: frame)
        setImage(UIImage(named: "jiahao"), forState: .Normal);
        layer.cornerRadius = width/2;
        layer.masksToBounds = true;
        let pin = UIPanGestureRecognizer(target: self, action: "pan:");
        addGestureRecognizer(pin);
        addView.addSubview(self)// 注意这句话我讲button添加到了addView上面,其实addView就是你将要button添加到那个View,因为手势到时候我们获取移动手势相对于的位置是button的父类,也就是addView
        AddView = addView;
    }
    

    实现滑动手势的方法

     func pan(pan:UIGestureRecognizer)
    {
        switch(pan.state){
        case .Began:
           
            self.center = pan.locationInView(AddView);// 获取位置,就是self的父类吧
            break;
        case .Changed:
            if self.selected
            {
                myBlock!(button: self);
                return;
            }
            let point = pan.locationInView(AddView!);
            self.center = point;
            break;
        case .Ended:
            
            if (self.center.x<0.5*kPalyhighWidth) {
                
                UIView.animateWithDuration(0.5, animations: { () -> Void in
                    self.x = 10;
                })
            }else{
                
                UIView.animateWithDuration(0.5, animations: { () -> Void in
                    self.x = kPalyhighWidth-10-self.width;
                })
            }
            break;
        default:
            break;
        }
    }
    

    目前可以达到的效果便是,因为考虑到按钮遮挡住界面的问题,所以手势完成后就将他移动到靠边的位置

    11.gif

    2.0我们可以看到移动的按钮点击的画会弹出四个晓得按钮分布到移动按钮的周围,那么我们就来试试,先实现按钮的点击创建出来view并出现按钮,这个不用多说先创建出来view,并加上按钮的话,按钮的位置不好加,这里我们用到了高中时学的正弦、余弦函数来计算,这里代码量比较大,我就一次粘贴过来了

    var myBlock:(()->())?// 使用与第二次点击消失动画
        {
            didSet{
                setMyBlock();
        }
    }
    var buttons_array:NSArray?// 创建出来几个按钮,以及按钮显示的内容等
        {
            didSet{
                if let buttonsArray = buttons_array
                {
                     angle = M_PI/2/Double(buttonsArray.count - 1);
                     self.setButtons_array(buttonsArray, angle: angle);
                }
                
            }
    }
    
    private func setButtons_array(array:NSArray,angle:Double){
        
        if self.subviews.count == 0
        {
            for var index in 0..<array.count
            {
                print(index);
                let button = UIButton(frame: CGRectMake(0, 0, 44, 44));
                let dict = array[index] as![String:String];
                
                button.setImage(UIImage(named:dict["icon"]!), forState:.Normal);
                button.setImage(UIImage(named:"\(dict["icon"]!)_select" ), forState: UIControlState.Selected);
                button.setTitle(dict["name"]!, forState: .Normal);
                button.backgroundColor = UIColor.whiteColor();
                button.setTitleColor(UIColor.blackColor(), forState: .Normal)
                button.setTitleColor(UIColor.redColor(), forState: .Selected);
                button.titleLabel?.font = UIFont.systemFontOfSize(10);
                button.tag = 998+index;
                button.addTarget(self, action: "buttonClick:", forControlEvents: UIControlEvents.TouchUpInside);
                button.titleEdgeInsets = UIEdgeInsetsMake(15, -25, -15, 0);
                button.imageEdgeInsets = UIEdgeInsetsMake(-5, 5, 5, -5);
                button.layer.cornerRadius = 22;
                button.layer.masksToBounds = true;
                if index == 3{
                    current_button = button;
                    button.selected = true;
                }
                button.enabled = false;
                starAnimattion(button,index: index)
                self.addSubview(button);
            }
        }else{
            for var index in 0..<self.subviews.count
            {
                let button = self.subviews[index] as! UIButton;
               starAnimattion(button,index: index)
            }
        }
    }
    func buttonClick(button:UIButton){//按钮的点击事件
        
        current_button?.selected = false;
        button.selected = true;
        current_button = button;
        self.deleagte?.dragViewDidButtonClick(button)
    
    }
    private func starAnimattion(button:UIButton,index:Int){// 按钮的展现动画
        let animation_one = CAKeyframeAnimation(keyPath: "position");
        animation_one.values = getAnimationValues(button, angle: angle, index: Double(index)) as [AnyObject];
        animation_one.duration = 1.0;
        
        let animatetionTwo = CAKeyframeAnimation(keyPath: "transform.scale");
        
        animatetionTwo.duration = 1.0;
        animatetionTwo.values = [0.0,0.2,0.4,0.6,0.8,1.0];
        
        let animatetion_three = CABasicAnimation(keyPath: "transform.rotation.z");
        animatetion_three.duration = 1.0;
        animatetion_three.toValue = NSNumber(double: 4.0*M_PI);
        
        let   group = CAAnimationGroup();
        group.animations = [animation_one,animatetionTwo,animatetion_three];
        group.duration = 1.0;
        group.removedOnCompletion = false;
        group.fillMode = kCAFillModeForwards;
        button.layer.addAnimation(group, forKey: nil);
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1.0 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
            button.enabled = true;
        }
    }
    private func setMyBlock(){// 按钮的消失动画
        
        if let buttonsArray = buttons_array
        {
            for var index in 0..<buttonsArray.count
            {
                let button = self.subviews[index] as! UIButton;
                
                let animation_one = CAKeyframeAnimation(keyPath: "position");
                let array = getAnimationValues(button, angle: angle, index: Double(index)) as [AnyObject];
                animation_one.values = [array.last!,array.first!];
                animation_one.duration = 1.0;
                
                let animatetionTwo = CAKeyframeAnimation(keyPath: "transform.scale");
                
                animatetionTwo.duration = 1.0;
                
                animatetionTwo.values = [1.0,0.8,0.6,0.4,0.2,0.0];
                
                let animatetion_three = CABasicAnimation(keyPath: "transform.rotation.z");
                animatetion_three.duration = 1.0;
                animatetion_three.toValue = NSNumber(double: 4.0*M_PI);
                
                let   group = CAAnimationGroup();
                group.animations = [animation_one,animatetionTwo,animatetion_three];
                group.duration = 1.0;
                group.removedOnCompletion = false;
                group.fillMode = kCAFillModeForwards;
                button.layer.addAnimation(group, forKey: nil);
            }
        }
    }
    private func getAnimationValues(button:UIButton,angle:Double,index:Double)->(NSArray){// 计算按钮的位置,最烦的地方,也是最考验逻辑的地方
        
        var one = CGPointZero;
        var two = CGPointZero;
        
        if buttonY < 0.4*kPalyhighHeight
        {
            
            print(cosf(Float(index*angle)),coshf(Float(index*angle)),index,angle)
           
            button.x = CGFloat(cosf(Float(index*angle)))*Radius
            button.y = CGFloat(sin(Float(index*angle)))*Radius
            one = CGPointMake(20, 20);
            two = CGPointMake(CGFloat(cosf(Float(index*angle)))*Radius+22, CGFloat(sin(Float(index*angle)))*Radius+22);
        }else{
            button.x = CGFloat(cosf(Float(-index*angle)))*Radius
            button.y = CGFloat(sin(Float(-index*angle)))*Radius+Padding+44
            one = CGPointMake(20, MaxWidth-20);
            two = CGPointMake(CGFloat(cosf(Float(-index*angle)))*Radius+22, CGFloat(sin(Float(-index*angle)))*Radius+22+Padding+44);
        }
        if (self.buttonX != 10) {
            button.x = CGFloat(cosf(Float(M_PI_2+index*angle)))*Radius+Padding+44;
            button.y = CGFloat(sin(Float(M_PI_2+index*angle)))*Radius;
            one = CGPointMake(MaxWidth-20,20);
            two = CGPointMake(CGFloat(cosf(Float(M_PI_2+index*angle)))*Radius+Padding+22+44, CGFloat(sin(Float(M_PI_2+index*angle)))*Radius+22);
            if (self.buttonY >= 0.4*kPalyhighHeight) {
                button.x = CGFloat(cosf(Float(M_PI+index*angle)))*Radius+Padding+44;
                button.y = CGFloat(sin(Float(M_PI+index*angle)))*Radius+Padding+44;
                one = CGPointMake(MaxWidth-20, MaxWidth-20);
                two = CGPointMake(CGFloat(cosf(Float(M_PI+index*angle)))*Radius+Padding+22+44, CGFloat(sin(Float(M_PI+index*angle)))*Radius+22+Padding+44);
            }
        }
        let oneValue = NSValue(CGPoint: one);
        let twoValue = NSValue(CGPoint: two);
        return [oneValue,twoValue];
    }
    

    3.0 也就是这样的四个状态,需要我们去做判断

    6A480177-55D2-4C45-9C1F-A287144B7EC3.png E7B6D06E-0772-4A3A-A3D5-C9DF03CD99F8.png
    9CDA25E2-9E99-4FFC-8466-9DFE8F21C245.png 43030027-3A55-4C8A-9489-57C8711D8346.png

    ![9CDA25E2-9E99-4FFC-8466-9DFE8F21C245.png]

    以上为这个demo的所有的技术要点,如过看的不是很好的画,那就直接来这里下载Demo吧!demo里面还有一些细节的处理,都是有注释的,有什么不懂的欢迎在下面评论,我会及时回复大家的,也是可以加我QQ:593216709。欢迎大家一起来探讨。

    相关文章

      网友评论

        本文标题:swfit的QQ抽屉和移动按钮代替一成不变TabbarVC的架构

        本文链接:https://www.haomeiwen.com/subject/ianjsttx.html