iOS13 swift模态弹出框适配-改进版

iOS 13,使用 presentViewController 方式打开视图,会跟导航栏有部分视觉差,如下图


iOS13 swift模态弹出框适配-改进版_第1张图片
Simulator Screen Shot - iPhone 8 - 2019-10-23 at 10.53.58.png

原因:苹果将 UIViewControllermodalPresentationStyle属性的默认值改成了新加的一个枚举值 UIModalPresentationAutomatic,对于多数UIViewController,此值会映射成 UIModalPresentationPageSheet

解决办法: 可以在vcpresent之前设置modalPresentationStyleUIModalPresentationStyle.fullScreen
另外,present的vc用户下拉可以dissmiss控制器,如果不想要这效果,可以这样设置vc.isModalInPresentation = true

注意:原来以UIModalPresentation.fullScreen样式弹出页面,那么这个页面弹出 ViewController 会依次调viewWillDisappearviewDidDisappear。然后在这个页面被 dismiss 的时候,将他弹出的那个 ViewControllerviewWillAppearviewDidAppear会被依次调用。然而使用默认的视差效果弹出页面,将他弹出的那个 ViewController 并不会调用这些方法,原先写在这四个函数中的代码以后都有可能会存在问题

改进:如果设置每个跳转都设置UIModalPresentation.fullScreen对代码的侵入性太强,这个时候可以使用方法交换来对进行扩展,具体代码如下:

swift
import Foundation
import UIKit

/**众所周知方法交换一般都在单例中进行,但是 OC中用来保证代码块只执行一次的dispatch_once在swfit中已经被废弃了,取而代之的是使用static let,let本身就带有线程安全性质的.
+load(): app启动的时候会加载所有的类,此时就会调用每个类的load方法.
+initialize(): 第一次初始化这个类的时候会被调用.

然而在目前的swift版本中这两个方法都不可用了,那现在我们要在这个阶段搞事情该怎么做? 例如method swizzling.
JORDAN SMITH大神给出了一种很巧解决方案.UIApplication有一个next属性,它会在applicationDidFinishLaunching之前被调用,这个时候通过runtime获取到所有类的列表,然后向所有遵循SelfAware协议的类发送消息.

*/
extension UIApplication {
    private static let runOnce: Void = {
        NothingToSeeHere.harmlessFunction()
    }()
    override open var next: UIResponder? {
        // Called before applicationDidFinishLaunching
        UIApplication.runOnce
        return super.next
    }
}
class NothingToSeeHere {
    static func harmlessFunction() {
        let typeCount = Int(objc_getClassList(nil, 0))
        let types = UnsafeMutablePointer.allocate(capacity: typeCount)
        let autoreleasingTypes = AutoreleasingUnsafeMutablePointer(types)
        objc_getClassList(autoreleasingTypes, Int32(typeCount))
        for index in 0 ..< typeCount {
            (types[index] as? SelfAware.Type)?.awake()
        }
        types.deallocate()
    }
}
protocol SelfAware:class {
    static func awake()
    static func swizzlingForClass(_ forClass:AnyClass,originalSelector:Selector,swizzledSelector:Selector)
}
extension SelfAware{
    static func swizzlingForClass(_ forClass:AnyClass,originalSelector:Selector,swizzledSelector:Selector){
        let originalMethod = class_getInstanceMethod(forClass, originalSelector)
        let swizzledMethod = class_getInstanceMethod(forClass, swizzledSelector)
        guard (originalMethod != nil && swizzledMethod != nil) else {
           return
        }
        if class_addMethod(forClass, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) {
           class_replaceMethod(forClass, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
        } else {
           method_exchangeImplementations(originalMethod!, swizzledMethod!)
        }
    }
}
extension UIViewController:SelfAware {
    static func awake() {
        presentSwizzledMethod
    }
    private static let presentSwizzledMethod:Void = {
        let originalSelector = #selector(present(_:animated:completion:))
        let swizzledSelector = #selector(cl_present(_:animation:completion:))
        swizzlingForClass(UIViewController.self, originalSelector: originalSelector, swizzledSelector: swizzledSelector)
    }()
    
    @objc func cl_present(_ controlelr:UIViewController,animation:Bool,completion: (() -> Void)? = nil) {
        print("swizzled_present")
        
        controlelr.modalPresentationStyle = UIModalPresentationStyle.fullScreen
        self.cl_present(controlelr,animation:true,completion:nil)
    }
}

以上就行关于适配的方法

你可能感兴趣的:(iOS13 swift模态弹出框适配-改进版)