美文网首页QiShare文章汇总
Swift 实现一个兼容iOS、tvOS、OSX的抽象层

Swift 实现一个兼容iOS、tvOS、OSX的抽象层

作者: QiShare | 来源:发表于2019-12-12 17:36 被阅读0次

    级别: ★☆☆☆☆
    标签:「iOS」「Swift」「抽象层」
    作者: dac_1033
    审校: QiShare团队


    有时开发一个工具包或者一个framework时,会要求兼容iOS、tvOS、OSX等苹果相关的平台,这些平台里的库、类及方法的名字和功能都很相近,如果想封装一套代码可以同时运行在三个平台上,那么就需要对相关的库、类及方法进行一个简单的抽象。在git上学习三方库源码的过程中,遇到过类似的实现,大致的抽象过程实现如下:

    import Foundation
    
    #if os(iOS) || os(tvOS)
    
    import UIKit
    
        // MARK: - 关于类型
    
        public typealias NSUIScreen = UIScreen
    
        public typealias NSUIFont = UIFont
        public typealias NSUIColor = UIColor
        public typealias NSUIEvent = UIEvent
        public typealias NSUITouch = UITouch
        public typealias NSUIImage = UIImage
    
        public typealias NSUIGestureRecognizer = UIGestureRecognizer
        public typealias NSUIGestureRecognizerState = UIGestureRecognizer.State
        public typealias NSUIGestureRecognizerDelegate = UIGestureRecognizerDelegate
        public typealias NSUITapGestureRecognizer = UITapGestureRecognizer
        public typealias NSUIPanGestureRecognizer = UIPanGestureRecognizer
    
    #if !os(tvOS)
        public typealias NSUIPinchGestureRecognizer = UIPinchGestureRecognizer
        public typealias NSUIRotationGestureRecognizer = UIRotationGestureRecognizer
    #endif
        
        public typealias NSUIDisplayLink = CADisplayLink
    
    
        // MARK: - 关于类型方法扩展
    
        extension NSUITapGestureRecognizer {
            
            @objc final func nsuiNumberOfTouches() -> Int {
                return numberOfTouches
            }
            
            @objc final var nsuiNumberOfTapsRequired: Int {
                get {
                    return self.numberOfTapsRequired
                }
                set {
                    self.numberOfTapsRequired = newValue
                }
            }
        }
        
        extension NSUIPanGestureRecognizer {
            
            @objc final func nsuiNumberOfTouches() -> Int {
                return numberOfTouches
            }
            
            @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint {
                return super.location(ofTouch: touch, in: inView)
            }
        }
        
    #if !os(tvOS)
        extension NSUIRotationGestureRecognizer
        {
            @objc final var nsuiRotation: CGFloat
            {
                get { return rotation }
                set { rotation = newValue }
            }
        }
    #endif
        
    #if !os(tvOS)
        extension NSUIPinchGestureRecognizer
        {
            @objc final var nsuiScale: CGFloat
            {
                get
                {
                    return scale
                }
                set
                {
                    scale = newValue
                }
            }
            
            @objc final func nsuiLocationOfTouch(_ touch: Int, inView: UIView?) -> CGPoint
            {
                return super.location(ofTouch: touch, in: inView)
            }
        }
    #endif
    
        extension UIView
        {
            @objc final var nsuiGestureRecognizers: [NSUIGestureRecognizer]?
            {
                return self.gestureRecognizers
            }
        }
        
        extension UIScreen
        {
            @objc final var nsuiScale: CGFloat
            {
                return self.scale
            }
        }
    
    
        // MARK: - 定义新类NSUIView
    
        open class NSUIView: UIView
        {
            public final override func touchesBegan(_ touches: Set<NSUITouch>, with event: NSUIEvent?)
            {
                self.nsuiTouchesBegan(touches, withEvent: event)
            }
    
            public final override func touchesMoved(_ touches: Set<NSUITouch>, with event: NSUIEvent?)
            {
                self.nsuiTouchesMoved(touches, withEvent: event)
            }
    
            public final override func touchesEnded(_ touches: Set<NSUITouch>, with event: NSUIEvent?)
            {
                self.nsuiTouchesEnded(touches, withEvent: event)
            }
    
            public final override func touchesCancelled(_ touches: Set<NSUITouch>, with event: NSUIEvent?)
            {
                self.nsuiTouchesCancelled(touches, withEvent: event)
            }
    
            @objc open func nsuiTouchesBegan(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
            {
                super.touchesBegan(touches, with: event!)
            }
    
            @objc open func nsuiTouchesMoved(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
            {
                super.touchesMoved(touches, with: event!)
            }
    
            @objc open func nsuiTouchesEnded(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
            {
                super.touchesEnded(touches, with: event!)
            }
    
            @objc open func nsuiTouchesCancelled(_ touches: Set<NSUITouch>?, withEvent event: NSUIEvent?)
            {
                super.touchesCancelled(touches!, with: event!)
            }
    
            @objc var nsuiLayer: CALayer?
            {
                return self.layer
            }
        }
    
    #endif
    
    
    #if os(OSX)
        import Cocoa
        import Quartz
    
      public typealias NSUIScreen = NSScreen
        
        public typealias NSUIFont = NSFont
        public typealias NSUIColor = NSColor
        public typealias NSUIEvent = NSEvent
        public typealias NSUITouch = NSTouch
        
        public typealias NSUIGestureRecognizer = NSGestureRecognizer
        public typealias NSUIGestureRecognizerState = NSGestureRecognizer.State
        public typealias NSUIGestureRecognizerDelegate = NSGestureRecognizerDelegate
        public typealias NSUITapGestureRecognizer = NSClickGestureRecognizer
        public typealias NSUIPanGestureRecognizer = NSPanGestureRecognizer
        public typealias NSUIPinchGestureRecognizer = NSMagnificationGestureRecognizer
        public typealias NSUIRotationGestureRecognizer = NSRotationGestureRecognizer
        
        
        public class NSUIDisplayLink
        {
            private var timer: Timer?
            private var displayLink: CVDisplayLink?
            private var _timestamp: CFTimeInterval = 0.0
            
            private weak var _target: AnyObject?
            private var _selector: Selector
            
            public var timestamp: CFTimeInterval
            {
                return _timestamp
            }
    
            init(target: AnyObject, selector: Selector)
            {
                _target = target
                _selector = selector
                
                if CVDisplayLinkCreateWithActiveCGDisplays(&displayLink) == kCVReturnSuccess
                {
                    
                    CVDisplayLinkSetOutputCallback(displayLink!, { (displayLink, inNow, inOutputTime, flagsIn, flagsOut, userData) -> CVReturn in
                        
                        let _self = unsafeBitCast(userData, to: NSUIDisplayLink.self)
                        
                        _self._timestamp = CFAbsoluteTimeGetCurrent()
                        _self._target?.performSelector(onMainThread: _self._selector, with: _self, waitUntilDone: false)
                        
                        return kCVReturnSuccess
                        }, Unmanaged.passUnretained(self).toOpaque())
                }
                else
                {
                    timer = Timer(timeInterval: 1.0 / 60.0, target: target, selector: selector, userInfo: nil, repeats: true)
                }
            }
            
            deinit
            {
                stop()
            }
    
            open func add(to runloop: RunLoop, forMode mode: RunLoopMode)
            {
                if displayLink != nil
                {
                    CVDisplayLinkStart(displayLink!)
                }
                else if timer != nil
                {
                    runloop.add(timer!, forMode: mode)
                }
            }
    
            open func remove(from: RunLoop, forMode: RunLoopMode)
            {
                stop()
            }
            
            private func stop()
            {
                if displayLink != nil
                {
                    CVDisplayLinkStop(displayLink!)
                }
                if timer != nil
                {
                    timer?.invalidate()
                }
            }
        }
        
        
        extension NSUITapGestureRecognizer
        {
            final func nsuiNumberOfTouches() -> Int
            {
                return 1
            }
            
            final var nsuiNumberOfTapsRequired: Int
            {
                get
                {
                    return self.numberOfClicksRequired
                }
                set
                {
                    self.numberOfClicksRequired = newValue
                }
            }
        }
    
        extension NSUIPanGestureRecognizer
        {
            final func nsuiNumberOfTouches() -> Int
            {
                return 1
            }
            
            /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures.
            final func nsuiLocationOfTouch(_ touch: Int, inView: NSView?) -> NSPoint
            {
                return super.location(in: inView)
            }
        }
        
        extension NSUIRotationGestureRecognizer
        {
            /// FIXME: Currently there are no velocities in OSX gestures, and not way to create custom touch gestures.
            final var velocity: CGFloat
            {
                return 0.1
            }
            
            final var nsuiRotation: CGFloat
            {
                get { return -rotation }
                set { rotation = -newValue }
            }
        }
        
        extension NSUIPinchGestureRecognizer
        {
            final var nsuiScale: CGFloat
            {
                get
                {
                    return magnification + 1.0
                }
                set
                {
                    magnification = newValue - 1.0
                }
            }
            
            /// FIXME: Currently there are no more than 1 touch in OSX gestures, and not way to create custom touch gestures.
            final func nsuiLocationOfTouch(_ touch: Int, inView view: NSView?) -> NSPoint
            {
                return super.location(in: view)
            }
        }
    
        extension NSView
        {
            final var nsuiGestureRecognizers: [NSGestureRecognizer]?
            {
                return self.gestureRecognizers
            }
        }
        
        extension NSScreen
        {
            final var nsuiScale: CGFloat
            {
                return self.backingScaleFactor
            }
        }
        
        
        open class NSUIView: NSView
        {
            public final override var isFlipped: Bool
            {
                return true
            }
    
            func setNeedsDisplay()
            {
                self.setNeedsDisplay(self.bounds)
            }
    
            
            public final override func touchesBegan(with event: NSEvent)
            {
                self.nsuiTouchesBegan(event.touches(matching: .any, in: self), withEvent: event)
            }
    
            public final override func touchesEnded(with event: NSEvent)
            {
                self.nsuiTouchesEnded(event.touches(matching: .any, in: self), withEvent: event)
            }
    
            public final override func touchesMoved(with event: NSEvent)
            {
                self.nsuiTouchesMoved(event.touches(matching: .any, in: self), withEvent: event)
            }
    
            open override func touchesCancelled(with event: NSEvent)
            {
                self.nsuiTouchesCancelled(event.touches(matching: .any, in: self), withEvent: event)
            }
    
            open func nsuiTouchesBegan(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
            {
                super.touchesBegan(with: event!)
            }
    
            open func nsuiTouchesMoved(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
            {
                super.touchesMoved(with: event!)
            }
    
            open func nsuiTouchesEnded(_ touches: Set<NSUITouch>, withEvent event: NSUIEvent?)
            {
                super.touchesEnded(with: event!)
            }
    
            open func nsuiTouchesCancelled(_ touches: Set<NSUITouch>?, withEvent event: NSUIEvent?)
            {
                super.touchesCancelled(with: event!)
            }
            
            open var backgroundColor: NSUIColor?
            {
                get
                {
                    return self.layer?.backgroundColor == nil
                        ? nil
                        : NSColor(cgColor: self.layer!.backgroundColor!)
                }
                set
                {
                    self.wantsLayer = true
                    self.layer?.backgroundColor = newValue == nil ? nil : newValue!.cgColor
                }
            }
    
            final var nsuiLayer: CALayer?
            {
                return self.layer
            }
        }
        
    #endif
    
    1. 用宏定义 #if 来判断os(iOS)、os(tvOS)、os(OSX),以区分不同平台;
    2. 在swift中,可以用typealias对不同平台上的类型起同样的别名,如NSUIFont作为别名分别对应UIFont和NSFont;
    3. 在swift中,可以用 extension 扩展类的方法以达到在不同平台上,类中方法同名的目的。例如在使用platform这个类的地方,调用NSUIScreen. nsuiScale;
    4. 在上述代码中,os(OSX)平台上没有的UIDisplayLink对应的雷,但是代码中全新定义了一个NSUIDisplayLink,NSUIDisplayLink中的方法也与UIDisplayLink中相应方法同名。

    这样,一个简单的跨平台抽象层就可以使用了,在需要这个platform时,如果你的代码里还用到其他夸端使用的类型,加载platform里就可以了。


    了解更多iOS及相关新技术,请关注我们的公众号:

    小编微信:可加并拉入《QiShare技术交流群》。

    关注我们的途径有:
    QiShare(简书)
    QiShare(掘金)
    QiShare(知乎)
    QiShare(GitHub)
    QiShare(CocoaChina)
    QiShare(StackOverflow)
    QiShare(微信公众号)

    推荐文章:
    iOS Password AutoFill
    iOS 给UILabel添加点击事件
    用SwiftUI给视图添加动画
    用SwiftUI写一个简单页面
    Swift 5.1 (7) - 闭包
    iOS App启动优化(三)—— 自己做一个工具监控App的启动耗时
    iOS App启动优化(二)—— 使用“Time Profiler”工具监控App的启动耗时
    iOS App启动优化(一)—— 了解App的启动流程
    奇舞周刊

    相关文章

      网友评论

        本文标题:Swift 实现一个兼容iOS、tvOS、OSX的抽象层

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