美文网首页程序员iOS Developer
Swift 3.0 商城开发 —— 商城上拉弹出层(仿淘宝)

Swift 3.0 商城开发 —— 商城上拉弹出层(仿淘宝)

作者: zZ爱吃菜 | 来源:发表于2017-04-14 17:22 被阅读970次

    写在前面

    在做项目的过程中,偶尔会转牛角尖,比如感觉很喜欢 京东 淘宝 的上拉弹出层获取商品属性的效果,于是就各种寻求思路,最终实现并封装成自己的类库,LWPopupViewController。将需要使用弹出层的 UIViewController 继承 LwPopupViewController 即可。简单易用。

    效果图

    我的项目效果图

    imageimage

    设计思路

    其实看起来无处下手的功能,分析起来很简单。

    组件

    1. 首先此功能继承于一个 ViewController,在其中定义两个子视图(maskView 和 popView)
    2. maskView 是这盖层视图,popView 是弹出层视图。

    原理

    弹出过程:整个 ViewController 被缩放形成缩小状态(具体动画后续讲解);将 maskView 添加到 主视图(UIApplication.shared.keyWindow?)中,形成遮挡层。popView 添加到 主视图(UIApplication.shared.keyWindow?)中并动态修改 frame 形成弹出动画效果。

    弹回过程:popView 通过修改 frame 在主视图中隐藏后,隐藏 maskView,同时 主 ViewController 从缩放状态回复到正常状态,最后将 maskView 和 popView 从应用主视图中移除。

    具体实现

    定义枚举类型

    巧用枚举类型,将很大提高代码的逻辑性

    // 主视图缩放 步骤
    enum LWAnimateType {
        case first
        case second
    }
    
    // 弹出层操作事件
    enum LWActionType {
        case popUp
        case popDown
    }
    

    定义控制器以及常规属性

    class LWPopController: UIViewController {
        
        // 主视图控制器——视图
        var rootView = UIView()
        // 遮挡层
        var maskView: UIView!
        // 弹出层视图
        var popView:UIView!
        
        // 弹出层的高度 默认:400
        var popViewHeight:CGFloat = SCREEN_HEIGHT * 4 / 5
        
        // 动画周期
        var duration: TimeInterval = 0.3
    
        // 具体实现代码 在下面
    }
    

    初始化 maskView 和 popView

    两个核心组件

    override func viewDidLoad() {
        super.viewDidLoad()
        if let selfNV = self.navigationController {
            rootView = selfNV.view
        } else {
            rootView = self.view
        }
        // 定义尺寸
        maskView = UIView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: SCREEN_HEIGHT))
        // 定义背景色
        maskView.backgroundColor = UIColor.black
        // 定义透明度
        maskView.alpha = 0.2
        // 添加点击事件
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(popDown))
        maskView.addGestureRecognizer(tapGesture)
        // 确保视图不被当前 UIView 视图遮挡
        maskView.layer.zPosition = CGFloat(INT8_MAX)
        // 定义尺寸
        popView = UIView(frame: CGRect(x: 0, y: SCREEN_HEIGHT, width: SCREEN_WIDTH, height: popViewHeight))
        /// 定义背景色
        popView.backgroundColor = UIColor.white
        /// 加个阴影
        popView.layer.shadowColor = UIColor.black.cgColor
        popView.layer.shadowOffset = CGSize(width: 0.5, height: 0.5)
        popView.layer.shadowOpacity = 0.8
        popView.layer.shadowRadius = 5
        // 确保视图不被当前 UIView 视图遮挡
        popView.layer.zPosition = CGFloat(INT8_MAX)
    }
    
    

    主视图 3D 设计(核心内容)

    如何实现 当前 UIViewController 缩放效果

    // 动画效果
    fileprivate func transformAnimation(type: LWAnimateType) -> CATransform3D {
        var transform = CATransform3DIdentity
        switch type {
        case .first:
            // 视图角度
            transform.m34 = -1.0 / 2000;
            // 尺寸缩小(transform对象,X轴,Y轴,Z轴)
            transform = CATransform3DScale(transform, 1, 1, 1)
            // 沿某轴旋转(transform对象,旋转角度,X轴,Y轴,Z轴)
            let angel = 15.0 * (CGFloat)(M_PI) / 180.0
            transform = CATransform3DRotate(transform, angel, 1, 0, 0)
        case .second:
            // 第二次 变形实在第一次的基础上
            transform.m34 = transformAnimation(type: .first).m34
            // 沿着某轴移动(transform对象,X轴,Y轴,Z轴)
            transform = CATransform3DTranslate(transform, 0, 10, 0)
            // 尺寸缩小(transform对象,X轴,Y轴,Z轴)
            transform = CATransform3DScale(transform, (SCREEN_WIDTH-40)/SCREEN_WIDTH, (SCREEN_HEIGHT-20)/SCREEN_HEIGHT, 1)
        }
        return transform
    }
    

    分析:3D 缩放分两步:

    1. 整个视图先沿着 X 轴旋转 15%;
    2. 整个视图再 X轴 左右个缩小 20尺寸,Y轴缩小 10尺寸;
    3D步骤一3D步骤一
    3D步骤二3D步骤二

    popView 设置 frame

    除了 当前 UIViewController 缩放动画,popView 也是需要弹出动画

    // 获取 popView 和 maskView 新的 frame
    func getViewFrame(type: LWActionType) -> CGRect {
        var frame:CGRect
        switch type {
        case .popUp:
            // popView 出现时的 frame
            frame = popView.frame
            frame.origin.y = SCREEN_HEIGHT - popViewHeight
        case .popDown:
            // popView 隐藏时的 frame
            frame = popView.frame
            frame.origin.y = SCREEN_HEIGHT
        }
        return frame
    }
    

    设计 弹出动画与 弹回动画

    通过 iOS 自动动画将 当前 UIViewController 缩放、maskView 遮挡以及popView 弹出结合起来

    // 弹出视图操作
    func popUp() {
        // 分别将 maskView 和 popView 添加到 应用主视图中,脱离与当前的 UIViewController 便于分离缩放动画
        UIApplication.shared.keyWindow?.addSubview(maskView)
        UIApplication.shared.keyWindow?.addSubview(popView)
        UIApplication.shared.keyWindow?.bringSubview(toFront: maskView)
        UIApplication.shared.keyWindow?.bringSubview(toFront: popView)
        // 获取最终的 popView 的弹出层位置尺寸,使用动画实现弹出效果
        let popViewFrame = getViewFrame(type: .popUp)
        UIView.animate(withDuration: self.duration,
                       delay: 0,
                       options: UIViewAnimationOptions.curveEaseInOut,
                       animations: {
                        // 当前 UIViewController 缩放动画一
                        self.rootView.layer.transform = self.transformAnimation(type: .first)
        }) { (bool) in
            UIView.animate(withDuration: self.duration, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
                // 当前 UIViewController 缩放动画二
                self.rootView.layer.transform = self.transformAnimation(type: .second)
                self.popView.frame = popViewFrame
            }, completion: nil)
        }
    }
    
    // 弹回操作
    func popDown() {
        self.maskView.removeFromSuperview()
        let popViewFrame = getViewFrame(type: .popDown)
        UIView.animate(withDuration: self.duration,
                       delay: 0,
                       options: UIViewAnimationOptions.curveEaseInOut,
                       animations: {
                        self.rootView.layer.transform = self.transformAnimation(type: .first)
        }) { (bool) in
            UIView.animate(withDuration: self.duration, delay: 0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
                self.popView.frame = popViewFrame
                self.rootView.layer.transform = CATransform3DIdentity
            }, completion: {(bool) in
                self.popView.removeFromSuperview()
            })
        }
    }
    

    使用说明

    其实我写这个类,用起来还是比较方便的,只需要将需要弹出的UIVeiwController 继承 LWPopViewController 即可,popView 将是父类属性,只需要在其中 addSubView 即可使用,通过 self.popUp() 和 self.popDown() 调用弹出、弹回事件。

    结合上一节的内容,简单写了一下调用代码

    class GoodsDetailController: LWPopController {
        
        
        var imageScrollView = ImageScrollView(frame: CGRect(x: 0, y: 0, width: SCREEN_WIDTH, height: 150))
        var data = [ImageScrollData]()
        
        override func viewDidLoad() {
            super.viewDidLoad()
            self.view.backgroundColor = UIColor.white
            edgesForExtendedLayout = .init(rawValue: 0)
            self.title = "图片无限滚动"
            self.view.addSubview(imageScrollView)
            for i in 1 ... 6 {
                let item = ImageScrollData(imageUrl: "image_scroll_0\(i).jpg", imageDescribe: nil)
                data.append(item)
            }
            imageScrollView.data = data
            self.popView.backgroundColor = .red
            initView()
        }
    
        
        func initView() {
            self.edgesForExtendedLayout = .init(rawValue: 0)
            self.view.addSubview(btnPopup)
            btnPopup.frame = CGRect(x: 10, y: imageScrollView.bottomY + 10, width: SCREEN_WIDTH - 20, height: 40)
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
        
        func showPop() {
            self.popUp()
        }
        
        func closePage() {
            self.dismiss(animated: true, completion: nil)
        }
        
        fileprivate var btnPopup: UIButton = {
            let object = UIButton()
            object.tag = 1
            object.layer.cornerRadius = 2
            object.backgroundColor = UIColor.green
            object.setTitle("图片无限滚动", for: .normal)
            object.setTitleColor(UIColor.black, for: .normal)
            object.addTarget(self, action: #selector(showPop), for:
                .touchUpInside)
            return object
        }()
    
    }
    
    

    最终效果

    3D步骤二3D步骤二

    相关文章

      网友评论

        本文标题:Swift 3.0 商城开发 —— 商城上拉弹出层(仿淘宝)

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