【发布时间】:2018-06-30 00:29:40
【问题描述】:
问题总结
我有一个通用视图子类TintableView<T>: UIView,它实现了一个具有相同关联类型T 的协议TintStateComputing。 TintStateComputing 的约束扩展实现没有被调用;而是调用其不受约束的扩展实现。
TintStateComputing 协议有一个函数computeTintState() -> T,它的作用类似于:检查本地状态属性,并返回T 的相应实例。
我想在TintStateComputing 上编写func computeTintState() -> T 的扩展实现,受限于T 的类型。例如,使用ControlState 枚举:
extension TintStateComputing where T == ControlState {
func computeTintState() -> T {
return self.isEnabled ? .enabled : T.default
}
}
但是,为了完成协议一致性,我想我需要考虑T 的其他值。所以我也声明了对TintStateComputing 的无约束扩展。 这个不受约束的扩展实现总是被调用,而不是受限的实现。
extension TintStateComputing {
func computeTintState() -> T {
return _tintState ?? T.default
}
}
游乐场试验台
import UIKit
// MARK: - Types
public enum ControlState: Int {
case normal, enabled, highlighted, selected, disabled
}
public protocol Defaultable {
static var `default`: Self { get }
}
extension ControlState: Defaultable {
public static let `default`: ControlState = .normal
}
// MARK: - TintStateComputing declaration
public protocol TintStateComputing {
associatedtype TintState: Hashable & Defaultable
func computeTintState() -> TintState
var _tintState: TintState? { get }
var isEnabled: Bool { get }
}
// MARK: - TintableView declaration
class TintableView<T: Hashable & Defaultable>: UIView, TintStateComputing {
// `typealias TintState = T` is implictly supplied by compiler
var _tintState: T?
var isEnabled: Bool = true { didSet { _tintState = nil }}
var tintState: T {
get {
guard _tintState == nil else {
return _tintState!
}
return computeTintState()
}
set {
_tintState = newValue
}
}
}
// MARK: - Unconstrained TintStateComputing extension
extension TintStateComputing {
func computeTintState() -> TintState {
return _tintState ?? TintState.default
}
}
// MARK: - Constrained TintStateComputing extension
extension TintStateComputing where TintState == ControlState {
func computeTintState() -> TintState {
return self.isEnabled ? .enabled : TintState.default
}
}
// MARK: - Test Case
let a = TintableView<ControlState>()
a.isEnabled = true
print("Computed tint state: \(a.tintState); should be .enabled") // .normal
print("finished")
解决方法
今天早上我意识到,因为(至少现在)我真正想要完成的是处理视图上的 isEnabled: Bool 标志,我可以遵循与 Defaultable 相同的模式来定义默认值“启用”案例。
public protocol Enableable {
static var defaultEnabled: Self { get }
}
extension ControlState: Defaultable, Enableable {
public static let `default`: ControlState = .normal
public static let defaultEnabled: ControlState = .enabled
}
到那时,我真的可以消除TintStateComputing 协议,并更新我的视图的tintState: T 实现以直接考虑标志。
var tintState: T {
get {
guard _tintState == nil else { return _tintState! }
return self.isEnabled ? T.defaultEnabled : T.default
}
set {
_tintState = newValue
}
}
它不像将实现放在一个受约束的扩展中那样通用,但它现在可以工作。我认为如果我将来有具有多维色调状态类型的子类(例如“启用”+“范围内”),我将能够通过override 解决。
struct CalendarState: Equatable, Hashable, Defaultable, Enableable {
let x: Int
static let `default`: CalendarState = CalendarState(x: 0)
static let defaultEnabled: CalendarState = CalendarState(x: 1)
}
class ControlTintableView: TintableView<ControlState> {}
class CalendarView: TintableView<CalendarState> {}
let a = ControlTintableView()
a.isEnabled = true
print("ControlTintableView computed tint state: \(a.tintState); should be: .enabled") // .enabled
let b = CalendarView()
b.isEnabled = true
print("CalendarView computed tint state: \(b.tintState); should be: CalendarState(x: 1)") // CalendarState(x: 1)
【问题讨论】:
标签: ios swift generics constraints