如何通过@AppStorage存储ClosedRange<Float>?遇初始化器匹配错误
使用@AppStorage存储ClosedRange的解决方案
问题描述
尝试用@AppStorage存储ClosedRange<Float>类型,已为该类型扩展了rawValue相关的初始化方法和属性,但在视图中声明:
@AppStorage("range_slider_position", store: .group) var sliderPosition: ClosedRange<Float> = 20...80
时触发错误,错误信息如下:
<view_definition_file...>.swift:16:3 No exact matches in call to initializer Candidate requires that the types 'ClosedRange<Float>' and 'Bool' be equivalent (requirement specified as 'Value' == 'Bool') (SwiftUI.AppStorage) Candidate requires that the types 'ClosedRange<Float>' and 'Int' be equivalent (requirement specified as 'Value' == 'Int') (SwiftUI.AppStorage) Candidate requires that the types 'ClosedRange<Float>' and 'Double' be equivalent (requirement specified as 'Value' == 'Double') (SwiftUI.AppStorage) Candidate requires that the types 'ClosedRange<Float>' and 'String' be equivalent (requirement specified as 'Value' == 'String') (SwiftUI.AppStorage) Candidate requires that the types 'ClosedRange<Float>' and 'URL' be equivalent (requirement specified as 'Value' == 'URL') (SwiftUI.AppStorage) Candidate requires that the types 'ClosedRange<Float>' and 'Data' be equivalent (requirement specified as 'Value' == 'Data') (SwiftUI.AppStorage)
原因分析
@AppStorage默认仅支持Bool、Int、Double、String、URL、Data这些基础类型,以及遵循RawRepresentable且原始值为上述基础类型的类型。你当前的扩展未明确让ClosedRange<Float>遵循RawRepresentable协议,导致@AppStorage无法识别该类型的存储逻辑。
解决方案
方案一:让ClosedRange遵循RawRepresentable
修改扩展代码,明确添加RawRepresentable协议遵循,确保原始值类型为String:
extension ClosedRange where Bound == Float: RawRepresentable { public init?(rawValue: String) { let components = rawValue.split(separator: ",").compactMap { Float($0) } guard components.count == 2 else { return nil } self = components[0]...components[1] } public var rawValue: String { return "\(lowerBound),\(upperBound)" } }
修改后即可直接使用最初的@AppStorage声明,@AppStorage会自动通过rawValue将ClosedRange<Float>序列化为String存储到UserDefaults中。
方案二:用String类型间接存储(更直观)
如果不想扩展协议,可通过存储对应字符串值,再用计算属性包装成ClosedRange<Float>:
// 存储原始字符串 @AppStorage("range_slider_position", store: .group) private var sliderPositionRaw: String = "20,80" // 对外暴露ClosedRange<Float>类型的计算属性 var sliderPosition: ClosedRange<Float> { get { // 解析字符串,失败则返回默认值 ClosedRange(rawValue: sliderPositionRaw) ?? 20...80 } set { // 将新范围转换为字符串存储 sliderPositionRaw = newValue.rawValue } }
这种方法无需修改类型协议,逻辑直观,还能避免潜在的协议冲突问题。
内容的提问来源于stack exchange,提问作者Konrad
相关产品推荐
相关产品推荐

