如何让自定义Between1and4属性包装器支持@Published?
问题描述
我有一个用于将变量值限制在1到4之间的@propertyWrapper结构体Between1and4,代码如下:
@propertyWrapper struct Between1and4 { internal static let lowest = CGFloat(1.0) internal static let highest = CGFloat(4.0) private var value: CGFloat var wrappedValue: CGFloat { get { return value } set { value = CGFloat(min(max(Between1and4.lowest, newValue), Between1and4.highest)) } } init(wrappedValue: CGFloat) { self.value = wrappedValue } }
现在想在ObservableObject类中创建一个带@Published的属性,要求该属性使用Between1and4的范围限制特性。尝试使用@Published @Between1and4 var scale: CGFloat时,出现以下错误:
Arguments to generic parameter 'Value' ('CGFloat' and 'Between1and4') are expected to be equal. Cannot convert value of type 'ReferenceWritableKeyPath<ContentViewModel, CGFloat>' to expected argument type 'ReferenceWritableKeyPath<ContentViewModel, Between1and4>'
解决方案
方法一:计算属性配合私有@Published属性
这种方法无需叠加属性包装器,通过私有发布属性+公开计算属性实现范围限制和更新发布,逻辑清晰易维护:
final class ContentViewModel: ObservableObject { // 私有@Published属性存储实际值 @Published private var _scale: CGFloat = 1.0 // 公开计算属性,自动应用范围限制 var scale: CGFloat { get { _scale } set { _scale = min(max(Between1and4.lowest, newValue), Between1and4.highest) } } }
使用时直接访问viewModel.scale即可,既会自动将值限制在1-4之间,又能触发ObservableObject的更新发布。
方法二:调整属性包装器的使用方式
如果坚持使用自定义属性包装器,可以将属性类型声明为Between1and4,让@Published直接包装该属性包装器实例,同时提供便捷访问的计算属性:
final class ContentViewModel: ObservableObject { @Published var scaleWrapper = Between1and4(wrappedValue: 1.0) // 便捷访问包装后的实际值 var scale: CGFloat { get { scaleWrapper.wrappedValue } set { scaleWrapper.wrappedValue = newValue } } }
错误原因说明
之前的写法报错是因为属性包装器叠加顺序导致类型不匹配:
@Between1and4会把CGFloat包装成Between1and4实例@Published会尝试包装这个Between1and4实例,此时它的泛型参数是Between1and4,但你声明的属性类型是CGFloat,两者不匹配,因此抛出错误。
内容的提问来源于stack exchange,提问作者Duck
相关产品推荐
相关产品推荐

