位移枚举在OC中非常常见也是非常方便运用的,举个例子SD_image中的SDWebImageOptions
1> NS_OPTIONS与位运算
NS_OPTIONS用来定义位移相关操作的枚举值,当一个枚举变量需要携带多种值的时候就需要,我们可以参考UIKit.Framework的头文件,可以看到大量的枚举定义。例如在SDWebImage下面就会接触到SDWebImageOptions枚举值:
typedef NS_OPTIONS(NSUInteger, SDWebImageOptions) {
SDWebImageRetryFailed = 1 << 0,
SDWebImageLowPriority = 1 << 1,
SDWebImageCacheMemoryOnly = 1 << 2,
SDWebImageProgressiveDownload = 1 << 3,
SDWebImageRefreshCached = 1 << 4,
SDWebImageContinueInBackground = 1 << 5,
SDWebImageHandleCookies = 1 << 6,
SDWebImageAllowInvalidSSLCertificates = 1 << 7,
SDWebImageHighPriority = 1 << 8,
SDWebImageDelayPlaceholder = 1 << 9,
SDWebImageTransformAnimatedImage = 1 << 10,
SDWebImageAvoidAutoSetImage = 1 << 11,
SDWebImageScaleDownLargeImages = 1 << 12
};
找了一篇位运算基础 https://baike.baidu.com/item/位运算/6888804
================分界线================
然而,在swift中使用位枚举的时候发现swift本身不支持原来的语法,我们先尝试原来的写作方法:
public enum Corner {
case topLeft = 1 << 1
case topRight = 1 << 2
case bottomLeft = 1 << 3
case bottomRight = 1 << 4
// case all
}
语法报错为
把Corner改为Int类型仍然解决不了问题,(enum Corner: Int).
swift 的枚举不支持运算值,只支持储存值,那试试吧位移值的具体值计算出来
public enum Corner: Int {
case topLeft = 2
case topRight = 4
case bottomLeft = 8
case bottomRight = 16
// case all
}
虽然不报错了,但是这种写法如果枚举值很多的话,他们之间的关系不够直观,既那然枚举值不能是计算值,那么我们给枚举添加一个值的属性。事例代码如下:
public enum RectCorner: RectCornersValue {
case topLeft
case topRight
case bottomLeft
case bottomRight
case all
}
extension RectCorner {
fileprivate var value: Int {
switch self {
case .topLeft: return 1 << 0
case .topRight: return 1 << 1
case .bottomLeft: return 1 << 2
case .bottomRight: return 1 << 3
case .all:
return RectCorner.topLeft.value | RectCorner.topRight.value | RectCorner.bottomLeft.value | RectCorner.bottomRight.value
}
}
}
我们先忽略RectCornersValue,给枚举RectCorner写了个私有的value属性,也就是位移枚举的计算值,
使枚举能使用位运算,需要重载运算符‘|’
infix operator | : precedence
precedencegroup precedence { associativity: left }
重载运算符‘|’后,运算值为Int类型,也就是意味着,方法接受枚举类型同时也可能是枚举位运算的Int类型,这里我们用协议扩展约束类型
public protocol RectCornersValue {}
extension Int: RectCornersValue {}
同时 enum RectCorner: RectCornersValue 枚举也要遵循协议约束,这样方法参数类型RectCornersValue支持枚举和枚举的位运算值。
下面举个例子给view的任意角添加圆角,方法声明如下:
func cornerSet (_ radius: CGFloat, corners: RectCornersValue) { }
当角的参数为组合枚举的时候如何检测组合的元素,这里需要位运算的知识,这里例子如下:
extension RectCornersValue {
fileprivate func isContains(_ objEnum: RectCorner) -> Bool {
if (self as? Int) != nil {
let value = self as! Int
return value & objEnum.value != 0
} else {
let value = (self as? RectCorner)?.value ?? 0
return value & objEnum.value != 0
}
}
}
完整的代码实例为
import Foundation
infix operator | : precedence
precedencegroup precedence { associativity: left }
public protocol RectCornersValue {}
extension Int: RectCornersValue {}
/// RectCorner
///
/// - topLeft: topLeft
/// - topRight: topRight
/// - bottomLeft: bottomLeft
/// - bottomRight: bottomRight
/// - all: all
public enum RectCorner: RectCornersValue {
case topLeft
case topRight
case bottomLeft
case bottomRight
case all
}
extension RectCorner {
fileprivate var value: Int {
switch self {
case .topLeft: return 1 << 0
case .topRight: return 1 << 1
case .bottomLeft: return 1 << 2
case .bottomRight: return 1 << 3
case .all:
return RectCorner.topLeft.value | RectCorner.topRight.value | RectCorner.bottomLeft.value | RectCorner.bottomRight.value
}
}
static func | (left: RectCornersValue , right: RectCorner) -> RectCornersValue {
if ((left as? Int) != nil) {
let leftValue: Int = (left as! Int)
return leftValue | right.value
} else {
let leftValue: RectCorner = (left as! RectCorner)
return leftValue.value | right.value
}
}
}
extension RectCornersValue {
fileprivate func isContains(_ objEnum: RectCorner) -> Bool {
if (self as? Int) != nil {
let value = self as! Int
return value & objEnum.value != 0
} else {
let value = (self as? RectCorner)?.value ?? 0
return value & objEnum.value != 0
}
}
}
extension UIView {
/// view任意圆角弧度设置
///
/// - Parameters:
/// - radius: 弧度半径
/// - corners: 角 RectCorner,支持位枚举
func cornerSet (_ radius: CGFloat, corners: RectCornersValue) {
self.setNeedsLayout()
self.layoutIfNeeded()
var radius = radius
let pathRef = CGMutablePath()
let bounds: CGRect = self.bounds
let allowdMaxRadius = min(bounds.size.width, bounds.size.height)/2.0
if radius > allowdMaxRadius { radius = allowdMaxRadius }
let leftMiddle = CGPoint(x: bounds.minX, y: bounds.midY)
let topLeft = CGPoint(x: bounds.minX, y: bounds.minY)
let topRight = CGPoint(x: bounds.maxX, y: bounds.minY)
let bottomLeft = CGPoint(x: bounds.minX, y: bounds.maxY)
let bottomRight = CGPoint(x: bounds.maxX, y: bounds.maxY)
let topMiddle = CGPoint(x: bounds.midX, y: bounds.minY)
let bottomMiddle = CGPoint(x: bounds.midX, y: bounds.maxY)
let rightMiddle = CGPoint(x: bounds.maxX, y: bounds.midY)
func drawLine(p1: CGPoint, p2: CGPoint, _ corner: RectCorner) {
if corners.isContains(corner) {
pathRef.addArc(tangent1End: p1,
tangent2End: p2,
radius: radius,
transform: .identity)
} else {
pathRef.addLine(to: p1)
pathRef.addLine(to: p2)
}
}
pathRef.move(to: leftMiddle)
drawLine(p1: topLeft, p2: topMiddle, .topLeft)
drawLine(p1: topRight, p2: rightMiddle, .topRight)
drawLine(p1: bottomRight, p2: bottomMiddle, .bottomLeft)
drawLine(p1: bottomLeft, p2: leftMiddle, .bottomRight)
let layer = CAShapeLayer()
layer.path = pathRef
self.layer.mask = layer
}
}
//用法: label.cornerSet (10.0, corners:RectCornersValue.topLeft | RectCornersValue.topRight)
总结:所运用知识有位运算,枚举,运算符重载,协议及协议约束等
总体来说,swift下的位枚举运用没有OC下简单,这里只是作为学习和研究使用。