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

2017-01-11  本文已影响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
    }

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

猜你喜欢

热点阅读