继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

MG--Swift3.x干货( 转屏 跳转 RunTime等)

Mg明明就是你
关注TA
已关注
手记 19
粉丝 2
获赞 7


  • ###appDelegate代码
######## appDelegate  ######## appDelegate   ######## appDelegate ######## ######## ########
// MARK: - 是否横屏
extension AppDelegate {
   @objc(application:supportedInterfaceOrientationsForWindow:) func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
         if isLandscape {
             return .all
         }else{
             return .portrait
         }
     }
}
  • ###控制器代码
 ######## 控制器  ######## 控制器   ######## 控制器 ######## ######## ########
    override func viewWillAppear(_ animated: Bool) {
        KAppDelegate.isLandscape = true
        // 强制横屏
        let value = UIInterfaceOrientation.landscapeLeft.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
        super.viewWillAppear(animated)
    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        //将试图还原为竖屏
        KAppDelegate.isLandscape = false
        let value = UIInterfaceOrientation.portrait.rawValue
        UIDevice.current.setValue(value, forKey: "orientation")
    }
    // 视图是否自动旋转
    override var shouldAutorotate : Bool {
        return true
    }
    override var supportedInterfaceOrientations : UIInterfaceOrientationMask {
        return [UIInterfaceOrientationMask.portrait, UIInterfaceOrientationMask.landscapeLeft]
    }
    /// size : 屏幕翻转后的新的尺寸;
    /// coordinator : 屏幕翻转过程中的一些信息,比如翻转时间等;
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        coordinator.animate(alongsideTransition: { [unowned self] (context) in
            let orient = UIApplication.shared.statusBarOrientation
            switch orient {
            case .portrait:
                MGLog(message: "Portrait")
                self.tableView.frame = CGRect(x: 0, y: navHeight, width: self.view.mg_width, height: self.view.mg_height - navHeight)
            case .landscapeLeft, .landscapeRight:
                MGLog(message: "LandscapeLeft")
                self.tableView.frame = CGRect(x: 0, y: navHeight/2, width: self.view.mg_width, height: MGScreenH - navHeight/2)
            default:
                MGLog(message: "Anything But Portrait")
                break
            }
        }) { (context) in
            MGLog(message:"rotation completed")
        }
        super.viewWillTransition(to: size, with: coordinator)
    }


  • #补充1: iOS10 APP内跳转至系统设置(如WIFI,Bluetooth…)
    • ###app-Prefs:root=+跳转名称
    • eg: 1、WIFi:app-Prefs:root=WIFI
    • e.g: 2、蜂窝网络:app-Prefs:root=MOBILE_DATA_SETTINGS_ID
    • eg:** 3、蓝牙:app-Prefs:root=Bluetooth **
    • eg:** 4、About:app-Prefs:root=General&path=About **
    • eg:跳转到自己的应用下面的设置 app-Prefs:root=Appid
无线局域网 App-Prefs:root=WIFI
蓝牙 App-Prefs:root=Bluetooth
蜂窝移动网络 App-Prefs:root=MOBILE_DATA_SETTINGS_ID
个人热点 App-Prefs:root=INTERNET_TETHERING
运营商 App-Prefs:root=Carrier
通知 App-Prefs:root=NOTIFICATIONS_ID
通用 App-Prefs:root=General
通用-关于本机 App-Prefs:root=General&path=About
通用-键盘 App-Prefs:root=General&path=Keyboard
通用-辅助功能 App-Prefs:root=General&path=ACCESSIBILITY
通用-语言与地区 App-Prefs:root=General&path=INTERNATIONAL
通用-还原 App-Prefs:root=Reset
墙纸 App-Prefs:root=Wallpaper
Siri App-Prefs:root=SIRI
隐私 App-Prefs:root=Privacy
Safari App-Prefs:root=SAFARI
音乐 App-Prefs:root=MUSIC
音乐-均衡器 App-Prefs:root=MUSIC&path=com.apple.Music:EQ
照片与相机 App-Prefs:root=Photos
FaceTime App-Prefs:root=FACETIME
guard let url = URL(string: "app-Prefs:root=Bluetooth") else { 
        return 
}
if UIApplication.shared.canOpenURL(url) {
        UIApplication.shared.openURL(url)
}


  • #补充2:导航栏和TabBar的appearance
    • ###听说这个坑了好多人,分享一下
 var tabBarItemAppear = UITabBarItem.appearance()
  if #available(iOS 9.0, *) {
            tabBarItemAppear = UITabBarItem.appearance(whenContainedInInstancesOf: [MainTabBarVC.classForCoder() as! UIAppearanceContainer.Type])
}
  • #补充3:RunTime 扩展封装方法 只需调用
    • ###获取所有的方法和属性
// MARK: - RunTime
extension NSObject {
    /**
     *   获取所有的方法和属性
     *   参数: 当前类
     */
    func mg_GetMethodAndPropertiesFromClass(cls: AnyClass) {
        debugPrint("方法========================================================")
        var methodNum: UInt32 = 0
        let methods = class_copyMethodList(cls, &methodNum)
        for index in 0..<numericCast(methodNum) {
            let met: Method = methods![index]!
            debugPrint("m_name: \(method_getName(met)!)")
//            debugPrint("m_returnType: \(String(utf8String: method_copyReturnType(met))!)")
//            debugPrint("m_type: \(String(utf8String: method_getTypeEncoding(met))!)")
        }
        debugPrint("属性=========================================================")
        var propNum: UInt32 = 0
        let properties = class_copyPropertyList(cls, &propNum)
        for index in 0..<Int(propNum) {
            let prop: objc_property_t = properties![index]!
            debugPrint("p_name: \(String(utf8String: property_getName(prop))!)")
//            debugPrint("p_Attr: \(String(utf8String: property_getAttributes(prop))!)")
        }        
        debugPrint("成员变量======================================================")
        var ivarNum: UInt32 = 0
        let ivars = class_copyIvarList(cls, &ivarNum)
        for index in 0..<numericCast(ivarNum) {
            let ivar: objc_property_t = ivars![index]!
            let name = ivar_getName(ivar)
            debugPrint("ivar_name: \(String(cString: name!))")
        }
    }
  • ###黑魔法(交换方法)
/** 
     *  交换方法
     *  参数: 当前类 ,原方法   要交换的方法
     */
    class func mg_SwitchMethod(cls: AnyClass, originalSelector: Selector, swizzledSelector: Selector) {        
        let originalMethod = class_getInstanceMethod(cls, originalSelector)
        let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector)      
        let didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod))        
        if didAddMethod {
            class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod))
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
      }
}


  • #补充4:获取系统所有的UIFont文字名称
func getSystemAllFontName() {
      for familyName in UIFont.familyNames {
          print("Family name: \(familyName)")
          for fontName in UIFont.fontNames(forFamilyName: familyName) {
              print("Font name: \(fontName)")
          }
      }
}
  • Family Name.png
  • FontName.png


  • #补充5:swift3.0以上 tableviewcell分割线显示不全解决方案
    • ###方案1:UITableViewCell.separatorInset和UIView.layoutMargins
    • 在viewDidLoad()添加一下代码
if tableView.responds(to:#selector(setter: UITableViewCell.separatorInset)) {
      tableView.separatorInset = UIEdgeInsets.zero
}        
if tableView.responds(to:#selector(setter: UIView.layoutMargins)) {
      tableView.layoutMargins = UIEdgeInsets.zero
}
  • tableview实现多以下代理
    func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
        if cell.responds(to: #selector(setter: UITableViewCell.separatorInset)) {
            cell.separatorInset = UIEdgeInsets.zero
        }
        if cell.responds(to:#selector(setter: UIView.layoutMargins)) {
            cell.layoutMargins = UIEdgeInsets.zero
        }
    }
  • ###方案2:draw(_ rect: CGRect),通过在底部绘制颜色
// MARK: - 系统方法
    override func awakeFromNib() {
        super.awakeFromNib()
        // 记得要设置contentView.backgroundColor为clear,否则颜色会被它挡住了
        self.contentView.backgroundColor = UIColor.clear
    }
override func draw(_ rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()!
        context.setFillColor(UIColor.white.cgColor)
        context.fill(rect)
        context.setFillColor(UIColor(white: 0.9, alpha: 1.0).cgColor)
        context.fill(CGRect(x: 0, y: rect.size.height - 3, width:rect.size.width, height: 3))
    }
  • ###方案3:朋友教的,重写frame,高度-x,露出背景色
override var frame: CGRect {
        didSet {
            var tmpFrame : CGRect = super.frame
            tmpFrame.size.height -= 3
            super.frame           = tmpFrame
        }
    }
  • #总结:方法一和方法二需要去掉系统的分割线tb.separatorStyle = UITableViewCellSeparatorStyle.none效果更佳
    ###OC方法改变Tableview分割线的边距
 //  tableView设置:
 //1.调整(iOS7以上)表格分隔线边距
    if ([self.tableView respondsToSelector:@selector(setSeparatorInset:)]) {
        self.tableView.separatorInset = UIEdgeInsetsZero;
    }
    //    2.调整(iOS8以上)view边距(或者在cell中设置preservesSuperviewLayoutMargins,二者等效)
    if ([self.tableView respondsToSelector:@selector(setLayoutMargins:)]) {
        self.tableView.layoutMargins = UIEdgeInsetsZero;
    }
// cell 设置:
    cell.preservesSuperviewLayoutMargins = NO;
    [cell setLayoutMargins:UIEdgeInsetsZero];

  • #补充6:Swift3.0纯代码添加约束 可供参考
    • 禁用autoresizing(重要)

    • 给需要设置约束的视图禁用autoresizing,禁用父视图autoresizing对子控件无效
    • #方法1:代码添加autolayout约束
        // 说明
        /*
             constraintWithItem:需要设置约束的view
             attribute:需要设置约束的位置
             relatedBy:约束的条件
             toItem:约束依赖目标
             attribute:依赖目标约束位置
             multiplier:配置系数
             constant:额外需要添加的长度
         */
        /*
             计算公式:redView.attribute = self.view.attribute * multiplier + constant;
             其中:=符号取决于relatedBy:参数
             typedef NS_ENUM(NSInteger, NSLayoutRelation) {
             NSLayoutRelationLessThanOrEqual = -1,   小于等于
             NSLayoutRelationEqual = 0,              等于
             NSLayoutRelationGreaterThanOrEqual = 1, 大于等于
             };
         */
        tableView.translatesAutoresizingMaskIntoConstraints = false
        let leftCon = NSLayoutConstraint(item: tableView, attribute: NSLayoutAttribute.left, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: NSLayoutAttribute.left, multiplier: 1.0, constant: 0)
        let rightCon = NSLayoutConstraint(item: tableView, attribute: .right, relatedBy: NSLayoutRelation.equal, toItem: self.view, attribute: .right, multiplier: 1.0, constant: 0)
        let topCon = NSLayoutConstraint(item: tableView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1.0, constant: 0)
        let bottomCon = NSLayoutConstraint(item: tableView, attribute: .bottom, relatedBy: .equal
            , toItem: self.view, attribute: .bottom, multiplier: 1.0, constant: 0)
        self.view.addConstraints([leftCon,rightCon,topCon,bottomCon])
  • #方法2:添加约束 VFL格式
    // 说明
    /VFL格式说明
    功能        表达式
    水平方向       H:
    垂直方向       V:
    Views        [view]
    SuperView      |
    关系         >=,==,<=
    空间,间隙       -
    优先级        @value
    -----------------------------------------------------
    /
    VisualFormat: VFL语句
    options: 对齐方式等,可以选择居中等
    metrics: VFL语句中使用到的一些变量
    views: VFL语句中使用到的一些控件*/
// 代码
tableView.translatesAutoresizingMaskIntoConstraints = false
var cons = NSLayoutConstraint.constraints(withVisualFormat: "H:|-0-[tableView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["tableView": tableView])
cons += NSLayoutConstraint.constraints(withVisualFormat: "V:|-0-[tableView]-0-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: ["tableView": tableView])
view.addConstraints(cons)
  • ####eg1: "V:|-20-[redView(==50)]"
    • 解释:redView(==50)是什么意思?V是代表垂直方向,垂直方向也就是高度,说明redView他的高度是50,同理如果是H开头呢,就是代表宽度 。-20-又是什么意思呢?距离父控件上边为20。
  • ####eg2:"V:[redView]-20-[blueView(==50)]"
    • 解释:同理blueView他的高度是50,V是代表垂直方向,而且blueView距离redView垂直距离为20
  • ####eg3:
let layout_frameView = ["frameView":frameView,"superView":self.view]
var frameView_constraints = NSLayoutConstraint.constraintsWithVisualFormat("H:[frameView(300.)]-(<=1)-[superView]", options: NSLayoutFormatOptions.AlignAllCenterY, metrics: nil, views: layout_frameView)
frameView_constraints = NSLayoutConstraint.constraintsWithVisualFormat("V:[frameView(200.0)]-(<=1)-[superView]", options: NSLayoutFormatOptions.AlignAllCenterX, metrics: nil, views: layout_frameView)
view.addConstraints(frameView_constraints)
  • 解释:frameView宽度300和高度为200并且居中显示

  • #补充7:Swift3.0控件存在屏幕底部,当键盘出现,控件出现在键盘上方

类似这个

    // MARK:- 系统方法
    override func viewDidLoad() {
        super.viewDidLoad()
        // 1.添加并且布局
        view.addSubview(toolBar)
        toolBar.snp_makeConstraints { (make) -> Void in
            make.bottom.left.right.equalTo(view)
            make.height.equalTo(44)
        }
        // 2.监听键盘尺寸的改变的通知
        MGNotificationCenter.addObserver(self, selector: #selector(self.keyboardFrameChange(noti:)), name: NSNotification.Name.UIKeyboardDidChangeFrame, object: nil)
    }
    /// 监听键盘尺寸的改变的方法
    @objc fileprivate func keyboardFrameChange(noti: NSNotification) {
        /*
         userInfo = {
         UIKeyboardAnimationCurveUserInfoKey = 7;
         UIKeyboardAnimationDurationUserInfoKey = "0.4";
         UIKeyboardFrameEndUserInfoKey = "NSRect: {{0, 365}, {375, 302}}";
         }
         */
        let duration = (noti.userInfo![UIKeyboardAnimationDurationUserInfoKey]! as AnyObject).doubleValue        
        let offsetY = (noti.userInfo![UIKeyboardFrameEndUserInfoKey]! as AnyObject).cgRectValue.origin.y - MGScreenH
        // toolBar: 是要挨着键盘的控件
        toolBar.snp.updateConstraints { (make) -> Void in
            make.bottom.equalTo(view.snp.bottom).offset(offsetY)
        }
        let CurveValue = noti.userInfo![UIKeyboardAnimationCurveUserInfoKey]! as! Int
        UIView.animate(withDuration: duration!, delay: 0, options: UIViewAnimationOptions(rawValue: 0), animations: { () -> Void in
            UIView.setAnimationCurve(UIViewAnimationCurve.init(rawValue: CurveValue)!)
            self.view.layoutIfNeeded()
        }, completion: nil)
    }


  • ###补充8:Swift3.0tableView右边标题按钮兰的研究,通过KVC和Runtime可以改变颜色,系统API默认有提供接口sectionIndexColorsectionIndexBackgroundColor。其他功能还没有研究到,要做这个其他功能自定义比较合适。(比如选中文字有颜色…等)
func test() {
       let indexView = tableView.value(forKeyPath: "_index")
            as? UIView
       indexView?.setValue(UIColor.darkGray, forKey: "_indexColor")
       indexView?.setValue(UIColor.randomColor(), forKey: "_indexTrackingBackgroundColor")
       indexView?.setValue(UIColor.randomColor(), forKey: "_indexBackgroundColor")
       let sel = NSSelectorFromString("endTrackingWithTouch:withEvent:")
       let _ = indexView?.perform(sel)
       self.mg_GetMethodAndPropertiesFromClass(cls: NSClassFromString("UITableViewIndex")!)
       (NSClassFromString("UITableViewIndex") as! UIView.Type).init()
}


  • #补充9:Swift3.0 URL的encode和decode
extension String {
    // MARK: - encoding 系统API
    /*
       URLFragmentAllowedCharacterSet  "#%<>[\]^`{|}
       URLHostAllowedCharacterSet      "#%/<>?@\^`{|}
       URLPasswordAllowedCharacterSet  "#%/:<>?@[\]^`{|}
       URLPathAllowedCharacterSet      "#%;<>?[\]^`{|}
       URLQueryAllowedCharacterSet     "#%<>[\]^`{|}
       URLUserAllowedCharacterSet      "#%/:<>?@[\]^`
   */
    /**   这个方法没有多大用,系统API已经提供了很多方法给我们如上
     *  URL 编码
     *   return 编码字符串
     */
    func encodeEscapesURL(value: String) -> String {
        let str:String = value
        let originalString = str as CFString
        let charactersToBeEscaped = "!*'();:@&=+$,/?%#[]" as CFString  //":/?&=;+!@#$()',*"    //转意符号
        //let charactersToLeaveUnescaped = "[]." as CFStringRef  //保留的符号
        let result =
            CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
                                                    originalString,
                                                    nil,    //charactersToLeaveUnescaped,
                charactersToBeEscaped,
                CFStringConvertNSStringEncodingToEncoding(String.Encoding.utf8.rawValue)) as NSString 
        return result as String
    }
    /**
     *  URL 解码
     *   return 解码字符串
     */
    func stringByURLDecode() -> String {
        if self.removingPercentEncoding != nil {
            return self.removingPercentEncoding!
        } else {
            let en: CFStringEncoding = CFStringConvertNSStringEncodingToEncoding(String.Encoding.utf8.rawValue)
            var decoded: String = self.replacingOccurrences(of: "+", with: " ")
            decoded = (CFURLCreateStringByReplacingPercentEscapesUsingEncoding(nil, (decoded as CFString),nil, en) as String)
            return decoded
        }
    }
}
"http://baidu.com".encodeEscapesURL(value: "http://你好baidu.com")
let urlCode = "http://你好baidu.com".addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPasswordAllowed)
urlCode?.stringByURLDecode()

打印结果.png


#补充10:In-Call Status Bar的监听及视图位置改变调整

  • ###监听通知方法
//监听状态栏的改变
MGNotificationCenter.addObserver(self, selector: #selector(self.statusBarChange(noti:)), name: NSNotification.Name.UIApplicationWillChangeStatusBarFrame, object: nil)
  • ###通知方法,执行一些相应的的操作(感觉主要对frame布局的UI影响较大)
@objc fileprivate func statusBarChange(noti: Notification) {
    // 获取变化参数
    let rectValue: NSValue? = (noti.userInfo?[UIApplicationStatusBarFrameUserInfoKey] as? NSValue)
    let statusRect: CGRect? = rectValue?.cgRectValue
    let statusFrame: CGRect = view.convert(statusRect!, from: MGKeyWindow)
    let statusHeight: CGFloat = statusFrame.size.height - 20
    // 需要修改的控件,举个:比如下面这个
    if statusHeight == 0 {
        settingBtn.frame = CGRect(x: 15, y: MGScreenH-64-44, width: 60, height: 44)
    } else {
        settingBtn.frame = CGRect(x: 15, y: MGScreenH-64-44-20, width: 60, height: 44)
    }
}

处理前.png
处理后.png


  • #补充11:Swift3.0 自定义UIButton替换导航返回按钮,仍然保持系统边缘返回手势
class BaseNavigationController: UINavigationController  {
        override func viewDidLoad() {
            super.viewDidLoad()
            // 1.保留局部返回手势
            self.interactivePopGestureRecognizer?.delegate = nil
            self.popDelegate = self.interactivePopGestureRecognizer?.delegate
            self.delegate = self
        }
}
// MARK: - UINavigationControllerDelegate
extension BaseNavigationController: UINavigationControllerDelegate {
    func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
        // 实现滑动返回功能
        // 清空滑动返回手势的代理就能实现
        if viewController == self.viewControllers[0] || viewController is ProdProgressItemListVC { // 可以控制那个控制器不需要局部返回手势
            self.interactivePopGestureRecognizer!.delegate = self.popDelegate
        } else {
            self.interactivePopGestureRecognizer!.delegate = nil
        }
    }
}

  • #补充12:解决Swift3.0 自定义全局返回手势和TableView左滑手势的冲突
class BaseNavigationController: UINavigationController  {
        override func viewDidLoad() {
            super.viewDidLoad()
            // 1.全局拖拽手势
            setUpGlobalPan()
        }
}
// MARK: - 全局拖拽手势
extension BaseNavigationController: UIGestureRecognizerDelegate {
    /// 全局拖拽手势
    fileprivate func setUpGlobalPan() {
        // 1.创建Pan手势
        let target = interactivePopGestureRecognizer?.delegate
        let globalPan = UIPanGestureRecognizer(target: target, action: Selector(("handleNavigationTransition:")))
        globalPan.delegate = self
        self.view.addGestureRecognizer(globalPan)
        
        // 2.禁止系统的手势
        navigationController?.interactivePopGestureRecognizer?.isEnabled = false
    }
    
    /// 什么时候支持全屏手势
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if self.childViewControllers.count != 1 {
            if (gestureRecognizer is UIPanGestureRecognizer) {
                if (self.topViewController != nil) && (self.view.gestureRecognizers!.contains(gestureRecognizer)) {
                    let tPoint: CGPoint = ((gestureRecognizer as? UIPanGestureRecognizer)?.translation(in: gestureRecognizer.view))!
                    if tPoint.x >= 0 {
                        let y: CGFloat = fabs(tPoint.y)
                        let x: CGFloat = fabs(tPoint.x)
                        let af: CGFloat = 30.0 / 180.0 * .pi  // tanf(Float(af))
                        let tf: CGFloat = tan(af)
                        return (y / x) <= tf
                    } else {
                        return false
                    }
                }
            }
            return true
        } else {
           return false
        }
    }
}

  • #补充13:判断是模拟器还是真机
  • ###Swift3.x 运行在模拟器还是真机的判断
struct Platform {
    static let isSimulator: Bool = {
        var isSim = false
        #if arch(i386) || arch(x86_64)
            isSim = true
        #endif
        return isSim
    }()
}
  • iOS Objective-C判断是模拟器还是真机

#if TARGET_IPHONE_SIMULATOR//模拟器
#elseif TARGET_OS_IPHONE//真机
#endif

  • #补充14:运行在后台继续执行任务,比如定时器计时
//后台任务
var backgroundTask:UIBackgroundTaskIdentifier! = nil
 func applicationDidEnterBackground(_ application: UIApplication) {
        //如果已存在后台任务,先将其设为完成
        if self.backgroundTask != nil {
            application.endBackgroundTask(self.backgroundTask)
            self.backgroundTask = UIBackgroundTaskInvalid
        }
        //如果要后台运行
        //注册后台任务
        self.backgroundTask = application.beginBackgroundTask(expirationHandler: {
            () -> Void in
            //如果没有调用endBackgroundTask,时间耗尽时应用程序将被终止
            application.endBackgroundTask(self.backgroundTask)
            self.backgroundTask = UIBackgroundTaskInvalid
        })
    }

  • #补充15:钟摆效果
    var imageView: UIImageView!
    override func viewDidLoad() {
        super.viewDidLoad()
        self.view.backgroundColor = UIColor.brown
        setUpInit()
    }
    func setUpInit() {
        self.view.layoutIfNeeded()
        imageView = UIImageView(image: #imageLiteral(resourceName: "user_img_qd.png"))
        view.addSubview(imageView)
        imageView.frame.origin = CGPoint(x: 240, y: -5)
        imageView.layer.anchorPoint = CGPoint(x: 0.5, y: 0)
        startAnimation()
    }
    func startAnimation() {
        imageView.layer.removeAllAnimations()
        // 旋转动画
        let anim = CABasicAnimation(keyPath: "transform.rotation.z")
        anim.fromValue = 7.5/4 * M_PI
        anim.toValue = 8.5/4 * M_PI
        // 悬浮
        let pathAnimation = CAKeyframeAnimation(keyPath: "position")
        pathAnimation.calculationMode = kCAAnimationPaced
        pathAnimation.fillMode = kCAFillModeBoth
//        pathAnimation.repeatCount = MAXFLOAT
//        pathAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
        let path = UIBezierPath(ovalIn: imageView.frame.insetBy(dx: 10, dy: imageView.frame.size.height - 2))
        pathAnimation.path = path.cgPath
        pathAnimation.duration = Double(arc4random_uniform(UInt32(8))) + 2.0
        // 放大动画
        let scaleX = CAKeyframeAnimation(keyPath: "transform.scale")
        scaleX.values   = [1.0, 1.1, 1.0];
        scaleX.keyTimes = [0.0, 0.5, 1.0];
        // 组动画
        let group = CAAnimationGroup()
        group.animations = [anim,pathAnimation,scaleX]
        group.duration = 2.0
        group.repeatCount = HUGE
        group.autoreverses = true
        imageView.layer.add(group, forKey: "group")
    }

效果.gif


  • #补充16:iOS判断过期或者想要延时x天后才执行某方法(Swift3.x和Objective-c)
    • ###登录是否过期(连续5天未登录App,就重新登录)Objective-c(iOS判断过期)
/** 判断用户登录的tocken是否过期   */
+(BOOL)checkTockenIsExpire {
            // 获取tocken日期
            NSUserDefaults *userDefault =  [NSUserDefaults standardUserDefaults];
            NSString *date = [userDefault valueForKeyPath:TockenIsExpireDateKey];
            NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
            formatter.dateFormat = @"yyyyMMdd";
            NSString *snow = [formatter stringFromDate:[NSDate date]];
            if (date == nil) { // tocken设置为连续超过5天不打开App就重新登录
                date = [formatter stringFromDate:[NSDate dateWithTimeIntervalSinceNow: 24 * 60 * 60 * 5]];
                [userDefault setValue: date forKeyPath: TockenIsExpireDateKey];
            }
            if ([snow compare:date] == NSOrderedDescending) { // 当前日期大于之前保存的日期 // 降序
                // 设置下一次过期的时间(5天有效)
                date = [formatter stringFromDate:[NSDate dateWithTimeIntervalSinceNow: 24 * 60 * 60 * 5]];
                [userDefault setValue: date forKeyPath: TockenIsExpireDateKey];
                return YES; // 过期
            }
            // 刷新过期的时间(5天有效)
            date = [formatter stringFromDate:[NSDate dateWithTimeIntervalSinceNow: 24 * 60 * 60 * 5]];
            [userDefault setValue: date forKeyPath: TockenIsExpireDateKey];
            return NO; // 没有过期
}
  • ###某方法审核的时候不要去执行,审核通过后才去执行该方法,Swift(想要延时x天后才执行某方法)
        // 判断今天是否执行某方法
        let defaultst = UserDefaults.standard
        var date = defaultst.object(forKey: kCurrentVersionDateKey) as? String    
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd"
        let snow = formatter.string(from: Date())
        if date == nil { // 第一次使用三天后才会有执行这个方法(跳过审核)
            date = formatter.string(from: Date(timeIntervalSinceNow: 24 * 60 * 60 * 3))
        }
        if snow.compare(date!) == .orderedAscending {
            return
        }  
        //  设置这个是为了以后每天都会执行方法
        defaultst.set(snow, forKey: kCurrentVersionDateKey)
        // 要执行的方法
        test()




  • #github

| 项目 | 简介 |
| :-------------:| :-------------: |
| MGDS_Swif | 逗视视频直播 |
| MGMiaoBo | 喵播视频直播 |
| MGDYZB | 斗鱼视频直播 |
| MGDemo | n多小功能合集 |
| MGBaisi | 高度仿写百思 |
| MGSinaWeibo | 高度仿写Sina |
| MGLoveFreshBeen | 一款电商App |
| MGWeChat | 小部分实现微信功能 |
| MGTrasitionPractice | 自定义转场练习 |
| DBFMDemo | 豆瓣电台 |
| MGPlayer | 一个播放视频的Demo |
| MGCollectionView | 环形图片排布以及花瓣形排布 |
| MGPuBuLiuDemo | 瀑布流–商品展 |
| MGSlideViewDemo | 一个简单点的侧滑效果,仿QQ侧滑 |
| MyResume | 一个展示自己个人简历的Demo |
| GoodBookDemo | 好书 |


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP