美文网首页
根据版本是否变动来显示对应启动页(Swift)

根据版本是否变动来显示对应启动页(Swift)

作者: 乂滥好人 | 来源:发表于2017-01-11 13:19 被阅读354次

    利用UICollectionView实现新特性启动页。没有新版本的情况下,程序运行展示默认启动图。附带跳过默认启动图。

    首次运行或有新版本时
    2017-01-11 13_52_45.gif
    import UIKit
    
    private let reuseIdentifier = "Cell"
    
    class FcNewfeaturesCollectionVC: UICollectionViewController {
    
        fileprivate let pageCount = 4
        fileprivate var layout:UICollectionViewFlowLayout = NewfeaturesLayout()
        
        /** 指示器 */
        fileprivate lazy var page: UIPageControl = {
            let page = UIPageControl()
            page.pageIndicatorTintColor = UIColor.lightGray
            page.currentPageIndicatorTintColor = UIColor.red
            page.isUserInteractionEnabled = false
            return page
        }()
        
        // 初始化
        init() {
            super.init(collectionViewLayout: layout)
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            self.view?.addSubview(self.page)
            self.page.numberOfPages = self.pageCount
            self.page.frame = CGRect(x: (UIScreen.main.bounds.size.width - 100) / 2, y: UIScreen.main.bounds.size.height - 80, width: 100, height: 10)
            
            self.collectionView!.register(FcNewfeaturesCell.self, forCellWithReuseIdentifier: reuseIdentifier)
        }
    
    
    // MARK: UICollectionViewDataSource
        override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            return pageCount
        }
    
        override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! FcNewfeaturesCell
            cell.imageIndex = indexPath.item
            return cell
        }
    
    // MARK: UICollectionViewDelegate
        // 完全显示cell之后调用
        override func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
            // 1.拿到当前显示的cell对应的索引
            let path = collectionView.indexPathsForVisibleItems.last!
            // 2、判断是否是最后一个cell
            if path.item == (pageCount - 1) {
                // 拿到当前索引对应的cell
                let cell = collectionView.cellForItem(at: path) as! FcNewfeaturesCell
                // 让cell执行按钮动画
                cell.startAnimation()
            }
        }
        
        // 监听collectionView的滚到
        override func scrollViewDidScroll(_ scrollView: UIScrollView) {
            // 1、获取滚动的偏移量 + scrollView.bounds.width * 0.5给偏移量加一半,当滑动一般就滚动pageControl的当前选中
            let offsetX = scrollView.contentOffset.x + scrollView.bounds.width * 0.5
            // 2、计算pageContra的currentIndex
            page.currentPage = Int(offsetX / scrollView.bounds.width) % self.pageCount
        }
        
    }
    
    
    // MARK - Cell
    class FcNewfeaturesCell: UICollectionViewCell {
        
        /** icon */
        fileprivate lazy var iconView = UIImageView()
        /** 进入按钮 */
        fileprivate lazy var startButton: UIButton = {
            let button = UIButton()
            button.setBackgroundImage(UIImage(named: "new_feature_button"), for: .normal)
            button.setBackgroundImage(UIImage(named: "new_feature_button_highlighted"), for: .highlighted)
            button.isHidden = true
            button.addTarget(self, action:#selector(butClick), for: .touchUpInside)
            return button
        }()
        
        /** 保存图片的索引 */
        fileprivate var imageIndex:Int = 0 {
            didSet{
                iconView.image = UIImage(named: "new_feature_\(imageIndex + 1)")
                startButton.isHidden = true
            }
        }
    // MARK - 系统回调
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupUI()
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
        
        /** 设置UI */
        fileprivate func setupUI(){
            // 1.添加子控件到CollectionView上
            contentView.addSubview(iconView)
            iconView.frame = contentView.bounds
            // 2、在最后一页添加 “进入按钮”
            contentView.addSubview(startButton)
            startButton.frame = CGRect(x: (UIScreen.main.bounds.size.width - 150) / 2, y: 500, width: 150, height: 40)
        }
        
        /** 进入按钮点击事件 */
        func butClick(){
            // 发送通知
            let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
            NotificationCenter.default.post(name: notifyChat, object: nil)
        }
        
        /** 让按钮开始动画*/
        func startAnimation(){
            startButton.isHidden = false
            // 执行动画
            startButton.transform = CGAffineTransform(scaleX: 0.0, y: 0.0)
            startButton.isUserInteractionEnabled = false
            // UIViewAnimationOptions(rawValue: 0) 等于 OC中 knilOptions
            UIView.animate(withDuration: 1.2, delay: 0.0, usingSpringWithDamping: 1.2, initialSpringVelocity: 6, options: UIViewAnimationOptions(rawValue: 0), animations: { () -> Void in
                // 还原,清空形变
                self.startButton.transform = CGAffineTransform.identity
                
                }, completion: { (_) -> Void in
                    self.startButton.isUserInteractionEnabled = true
            })
        }
    }
    
    
    // MARK - Layout
    class NewfeaturesLayout: UICollectionViewFlowLayout {
        
        override func prepare() {
            // 1.设置layout布局
            itemSize = UIScreen.main.bounds.size
            minimumInteritemSpacing = 0
            minimumLineSpacing = 0
            scrollDirection = .horizontal
            // 2.设置CollectionView的属性
            collectionView?.showsHorizontalScrollIndicator = false
            collectionView?.bounces = false
            collectionView?.isPagingEnabled = true
        }
    }
    

    无新特性展示默认启动图,自动跳转主页

    1、普通倒计时跳过


    2017-01-11 12_58_16.gif
    import UIKit
    /** 倒计时总时间 */
    private let kCountdownTime:Int = 5
    
    class FcNormalWecomVC: UIViewController {
        
    // MARK - 懒加载区
        /** 背景容器 */
        fileprivate lazy var bgView: UIView = {
            let view = UIView()
            view.backgroundColor = UIColor.white
            return view
        }()
        /** 启动图 */
        fileprivate lazy var iconImageView: UIImageView = {
            let icon = UIImageView()
            icon.image = UIImage(named: "123")
            return icon
        }()
        /** 跳过按钮 */
        fileprivate lazy var skipButton: UIButton = {
            let skipBut = UIButton()
            skipBut.layer.cornerRadius = 5
            skipBut.layer.borderWidth = 1
            skipBut.layer.borderColor = UIColor.red.cgColor
            skipBut.frame = CGRect(x: self.view.frame.size.width * 0.78, y: 20, width: 80, height: 25)
            skipBut.backgroundColor = UIColor.clear
            skipBut.setTitle("跳过(5s)", for: .normal)
            skipBut.setTitleColor(UIColor.red, for: .normal)
            skipBut.addTarget(self, action: #selector(normalButClick), for: .touchUpInside)
            return skipBut
        }()
        
    // MARK - 属性区
        /** 剩余时间 */
        var remainingTime: Int = 0 {
            willSet{
                skipButton.setTitle("跳过\(newValue)s", for: .normal)
                if newValue <= 0 {
                    isCounting = false
                }
            }
        }
        /** 计时器 */
        var timer: Timer?
        /** 按钮开关 */
        var isCounting:Bool = false {
            willSet {
                if newValue {
                    /** 初始化计时器 */
                    timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
                    remainingTime = kCountdownTime
                }else {
                    // 暂停且销毁计时器
                    timer?.invalidate()
                    timer = nil
                }
            }
        }
        
    // MARK - 系统回调
        override func viewDidLoad() {
            super.viewDidLoad()
            setupUI()
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            // 延迟5秒执行操作
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(kCountdownTime)) {
                
                UIView .animate(withDuration: 0.6, animations: {
                    self.bgView.alpha = 0.3
                    self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
                    
                    }, completion: { (bool) in
                        self.bgView.alpha = 0
                        let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
                        NotificationCenter.default.post(name: notifyChat, object: nil)
                })
            }
        }
    }
    
    extension FcNormalWecomVC {
        
        /** 设置UI */
        func setupUI() {
            view.backgroundColor = UIColor.white
            view.addSubview(bgView)
            self.bgView.frame = view.bounds
            self.bgView.addSubview(iconImageView)
            self.iconImageView.frame = self.bgView.bounds
            self.bgView.addSubview(skipButton)
            isCounting = true
        }
    }
    
    extension FcNormalWecomVC {
        
        /** 跳过按钮点击事件 */
        func normalButClick(){
            UIView .animate(withDuration: 0.6, animations: {
                self.bgView.alpha = 0.3
                self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
                
                }, completion: { (bool) in
                    self.bgView.alpha = 0
                    // 发送通知
                    let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
                    NotificationCenter.default.post(name: notifyChat, object: nil)
            })
        }
        
        /** 更新时间 */
        func updateTime(timer: Timer){
            remainingTime -= 1
        }
    }
    

    2、带进度条倒计时跳过


    2017-01-11 16_29_10.gif
    import UIKit
    /** 倒计时总时间 */
    private let kCountdownTime:Int = 8
    /** 线宽 */
    private let kLineWidth:CGFloat = 2.0
    
    class FcNormalWecomVC: UIViewController {
        /** 带进度按钮计时器 */
        var countView: CountDownView?
        
        /** 背景容器 */
        fileprivate lazy var bgView: UIView = {
            let view = UIView()
            view.backgroundColor = UIColor.white
            return view
        }()
        /** 启动图 */
        fileprivate lazy var iconImageView: UIImageView = {
            let icon = UIImageView()
            icon.image = UIImage(named: "123")
            return icon
        }()
    
    // MARK:- 系统回调
        override func viewDidLoad() {
            super.viewDidLoad()
            setupUI()
        }
        
        override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
            // 延迟5秒执行操作
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + Double(kCountdownTime)) {
                
                UIView .animate(withDuration: 0.6, animations: {
                    self.bgView.alpha = 0.3
                    self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
                    
                    }, completion: { (bool) in
                        self.bgView.alpha = 0
                        let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
                        NotificationCenter.default.post(name: notifyChat, object: nil)
                })
            }
        }
    }
    
    // MARK:- 设置UI
    extension FcNormalWecomVC {
        
        fileprivate func setupUI() {
            view.backgroundColor = UIColor.white
            view.addSubview(bgView)
            self.bgView.frame = view.bounds
            self.bgView.addSubview(iconImageView)
            self.iconImageView.frame = self.bgView.bounds
            
            // 跳过按钮
            let progressBut = UIButton()
            progressBut.frame = CGRect(x: self.view.frame.size.width - 50 - 35 , y: 20, width: 50, height: 25)
            progressBut.titleLabel?.font = UIFont.systemFont(ofSize: 15)
            progressBut.setTitleColor(UIColor.red, for: .normal)
            progressBut.setTitle("跳 过", for: .normal)
            progressBut.backgroundColor = UIColor.clear
            progressBut.addTarget(self, action: #selector(progressButtonCilck), for: .touchUpInside)
            self.bgView.addSubview(progressBut)
    
            // 圈圈
            let viewFrame = CGRect(x: self.view.frame.size.width - 35, y: 20, width: 25, height: 25)
            let countDownView = CountDownView(frame: viewFrame, totalTime: kCountdownTime, lineWidth: kLineWidth, lineColor: UIColor.red, textFontSize: 2, startFinishCallback: {
                progressBut.isHidden = false
                
                }, completeFinishCallback: {
                    progressBut.isHidden = true
            })
            self.bgView.addSubview(countDownView)
            // 开始倒计时
            countDownView.startTheCountDown()
        }
    }
    
    // MARK:- 事件处理
    extension FcNormalWecomVC {
        
        func progressButtonCilck(){
            UIView .animate(withDuration: 0.6, animations: {
                self.bgView.alpha = 0.3
                self.bgView.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
                
                }, completion: { (bool) in
                    self.bgView.alpha = 0
                    // 发送通知
                    let notifyChat = NSNotification.Name(FcSwitchRootViewControllerKey)
                    NotificationCenter.default.post(name: notifyChat, object: nil)
            })
        }
    }
    

    (PS:带进度按倒计时涉及到另外一个自定义类,代码如下)

    import UIKit
    
    class CountDownView: UIView {
    
        // MARK:- 属性定义
        var startBack: (() -> ())?
        var completeBack: (() -> ())?
        var progressLineWidth: CGFloat?
        var progressLineColor: UIColor?
        var countLb: UILabel?
        var totalTimes: NSInteger = 0
        var isCountDown:Bool = false
        
        // MARK:- 懒加载
        fileprivate lazy var progressLayer:CAShapeLayer = {
            let progress = CAShapeLayer()
            let kWidth = self.frame.size.width
            let kHeight = self.frame.size.height
            progress.frame = CGRect(x: 0, y: 0, width: kWidth, height: kHeight)
            progress.position = CGPoint(x: kWidth/2.0, y: kHeight/2.0)
            let point = CGPoint(x: kWidth/2.0, y: kHeight/2.0)
            progress.path = UIBezierPath(arcCenter: point, radius: (kWidth - self.progressLineWidth!)/2.0, startAngle: 0, endAngle: CGFloat(M_PI * 2), clockwise: true).cgPath
            progress.fillColor = UIColor.clear.cgColor
            progress.lineWidth = self.progressLineWidth!
            progress.strokeColor = self.progressLineColor!.cgColor
            progress.strokeEnd = 0
            progress.strokeStart = 0
            progress.lineCap = kCALineCapRound
            return progress
        }()
    
        fileprivate lazy var rotateAnimation:CABasicAnimation = {
            let rotateAnima = CABasicAnimation(keyPath: "transform.rotation.z")
            rotateAnima.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            rotateAnima.fromValue = CGFloat(2 * M_PI)
            rotateAnima.toValue = CGFloat(0)
            rotateAnima.duration = CFTimeInterval(self.totalTimes)
            rotateAnima.isRemovedOnCompletion = false
            return rotateAnima
        }()
        
        fileprivate lazy var strokeAnimationEnd:CABasicAnimation = {
            let strokeAnimation = CABasicAnimation(keyPath: "strokeEnd")
            strokeAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
            strokeAnimation.duration = CFTimeInterval(self.totalTimes)
            strokeAnimation.fromValue = CGFloat(1)
            strokeAnimation.toValue = CGFloat(0)
            strokeAnimation.speed = Float(1.0)
            strokeAnimation.isRemovedOnCompletion = false
            return strokeAnimation
        }()
        
        fileprivate lazy var animationGroup:CAAnimationGroup = {
            let group = CAAnimationGroup()
            group.animations = [self.strokeAnimationEnd, self.rotateAnimation]
            group.duration = CFTimeInterval(self.totalTimes)
            return group
        }()
        
        /**
         *  初始化定时器
         frame         位置
         totalTime     总时间
         lineWidth     线宽
         lineColor     线色
         fontSize      字体大小(控件的宽度/多少)
         startFinishCallback    开始闭包
         completeFinishCallback 完成闭包
         */
        init(frame: CGRect, totalTime: NSInteger, lineWidth:CGFloat, lineColor:UIColor, textFontSize:CGFloat, startFinishCallback:@escaping () -> (), completeFinishCallback:@escaping () -> ()) {
            super.init(frame: frame)
            startBack = startFinishCallback
            completeBack = completeFinishCallback
            progressLineWidth = lineWidth
            progressLineColor = lineColor
            totalTimes = totalTime
            layer.addSublayer(progressLayer)
            createLabel(textFontSize: textFontSize)
        }
        
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
    // MARK:- 逻辑处理
    extension CountDownView {
        
        /** 创建label */
        fileprivate func createLabel(textFontSize: CGFloat) {
            let label = UILabel()
            label.frame = self.bounds
            label.textColor = self.progressLineColor
            label.font = UIFont.systemFont(ofSize: self.frame.size.width / textFontSize)
            label.textAlignment = .center
            addSubview(label)
            countLb = label
        }
        
        /** 创建定时器 */
        fileprivate func createTimer() {
            weak var weakSelf = self
            if totalTimes > 0 {
                var timeout = totalTimes
                // 1、获取一个全局队列
                let queue = DispatchQueue.global()
                // 2、创建间隔定时器
                let time = DispatchSource.makeTimerSource(flags: [], queue: queue)
                time.scheduleRepeating(deadline: .now(), interval: .seconds(1), leeway: .microseconds(100))
                time.setEventHandler(handler: {
                    if timeout <= 0 {
                        // 2.1定时器取消
                        time.cancel()
                        // 2.2主线程刷新UI
                        DispatchQueue.main.async(execute: {
                            weakSelf?.isCountDown = false
                            weakSelf?.isHidden = true
                            weakSelf?.stopCountDown()
                        })
                    }else {
                        weakSelf?.isCountDown = true
                        let seconds = timeout % 60
                        DispatchQueue.main.async(execute: {
                            self.countLb?.text = "\(String(seconds))s"
                        })
                        timeout -= 1
                    }
                })
                // 3、复原定时器
                time.resume()
            }
        }
        
        /** 开始倒计时 */
        func startTheCountDown(){
            self.isHidden = false
            isCountDown = true
            if (startBack != nil) {
                startBack!()
            }
            createTimer()
            progressLayer.add(animationGroup, forKey: "group")
        }
        
        /** 停止倒计时 */
        fileprivate func stopCountDown(){
            progressLayer.removeAllAnimations()
            if completeBack != nil {
                completeBack!()
            }
        }
    }
    

    普通、带进度条按钮都支持点击 跳过 进行跳转
    2017-01-11 12_58_59.gif
    APPdelegate中代码
    import UIKit
    // 切换控制器通知
    let FcSwitchRootViewControllerKey = "FcSwitchRootViewControllerKey"
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: UIWindow?
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
            
            // 接收通知
            NotificationCenter.default.addObserver(self, selector: #selector(switchRootViewController(notify:)), name: NSNotification.Name( FcSwitchRootViewControllerKey), object: nil)
                    
            window = UIWindow(frame: UIScreen.main.bounds)
            window?.backgroundColor = UIColor.white
            window?.rootViewController = defaultController()
            window?.makeKeyAndVisible()
            
            isNewUpdate()
            return true
        }
        
        /** 实现通知方法 */
        func switchRootViewController(notify: NSNotification) {
            window?.rootViewController = ViewController()
        }
        
        /** 销毁通知 */
        deinit {
            NotificationCenter.default.removeObserver(self)
        }
        
        /** 用于获取默认显示界面 */
        fileprivate func defaultController() -> UIViewController {
            return isNewUpdate() ? FcNewfeaturesCollectionVC() : FcNormalWecomVC()
        }
        
        
        /** 检查是否有新版本 */
        // 此修饰词作用为消除 "Result of call to ‘isNewUpdate()’is unused" 没有使用返回结果的警告。
        @discardableResult
        fileprivate func isNewUpdate() -> Bool {
            let key = "CFBundleShortVersionString"
            // 1.获取应用程序 -> 当前版本 info.plist
            let currentVersion = Bundle.main.infoDictionary![key] as! String
            //        print("当前版本 -> \(currentVersion)")
            // 2.获取沙河应用程序版本 -> 从本地文件读取(以前自己存储)  String ?? "0.0" 如果String是空,则取 0.0
            let sandboxVersion = UserDefaults.standard.value(forKey: key) as? String ?? "0.0"
            //        print("老版本 -> \(sandboxVersion)")
            // 3.利用当前版本 和 以前版本比较
            // 2.0                     1.0                                  降序
            if currentVersion.compare(sandboxVersion) == ComparisonResult.orderedDescending{
                // 3.1如果有新版本, 将新版本保存
                let defaults = UserDefaults.standard
                defaults.setValue(currentVersion, forKey: key)
                // iOS 7.0 之后,就不需要同步了,iOS 6.0 之前,如果不同步不会第一时间写入沙盒
                defaults.synchronize()
                // 4.返回时候有新版本
                return true
            }
            return false
        }
    

    当版本号增加之后运行才能再次看到新特性

    相关文章

      网友评论

          本文标题:根据版本是否变动来显示对应启动页(Swift)

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