Swift 实现一个兼容iOS、tvOS、OSX的抽象层
2019-12-12 本文已影响0人
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
- 用宏定义 #if 来判断os(iOS)、os(tvOS)、os(OSX),以区分不同平台;
- 在swift中,可以用typealias对不同平台上的类型起同样的别名,如NSUIFont作为别名分别对应UIFont和NSFont;
- 在swift中,可以用 extension 扩展类的方法以达到在不同平台上,类中方法同名的目的。例如在使用platform这个类的地方,调用NSUIScreen. nsuiScale;
- 在上述代码中,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的启动流程
奇舞周刊