译自www.hackingwithswift.com/books/ios-s…
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀
SwiftUI 使得创建自定义 UI 组件变得十分容易,因为它们基本上就是一些暴露了某些@Binding
给我们的视图。
为了说明这一点,我们要构建一个星级评价视图,可以让用户通过点击图片选择 1 分 到 5 分。虽然在我们的案例中,只需要很简单的实现就能工作,不过为了让这个控件也能适用于别的地方,我们需要为它添加一些灵活性。这意味着我们需要设计几个自定义属性:
nil
作为未点亮的图片,一个填充的星星作为点亮的图片;如果我们发现未点亮用的也是 nil
,那我们对其也适用点亮的图片)我们还需要一个额外的属性存储@Binding
整数,以便我们报告用户选择的星级。
创建一个新的 SwiftUI 视图,取名 “RatingView”,添加下面这些属性:
@Binding var rating: Int var label = "" var maximumRating = 5 var offImage: Image? var onImage = Image(systemName: "star.fill") var offColor = Color.gray var onColor = Color.yellow复制代码
在我们填充body
属性之前,请先尝试编译代码 —— 你会发现编译失败,因为RatingView_Previews
结构体没有为rating
传入Binding
。
SwiftUI 对此有一个专门的简单解决方案,称为常量 bindings。它们是一些拥有固定值的 bindings ,一方面意味着它们不能在 UI 中改变,同时也意味着我们可以非常方便地创建它们 —— 对于预览的场景非常适用。
把previews
属性替换成下面这样:
static var previews: some View { RatingView(rating: .constant(4)) }复制代码
现在让我们回到body
属性。这里头会有一个HStack
,包含要显示的标签,加上足够多的星星 —— 不过,我们可以选择自己的图片,当然不一定必须是星星。
给星星选择图片的逻辑非常简单,但将它们拆分到自己的方法能够降低代码的复杂度。逻辑如下:
我们可以把这个逻辑封装成一个方法,把下面的代码添加到RatingView
:
func image(for number: Int) -> Image { if number > rating { return offImage ?? onImage } else { return onImage } }复制代码
现在,实现body
属性变得令人惊讶的简单。如果标签有文本,则使用为其创建文本视图,然后使用ForEach
遍历 1 到最大等级,然后调用重复调用image(for:)
。取决于评级,我们还要应用一个前景色,并且增加点击手势,以支持选定等级。
把body
属性替换成下面这样:
HStack { if label.isEmpty == false { Text(label) } ForEach(1...maximumRating) { number in self.image(for: number) .foregroundColor(number > self.rating ? self.offColor : self.onColor) .onTapGesture { self.rating = number } } }复制代码
这样一来我们的评级视图就完成了,把它放回我们的AddBookView
,用下面的代码替换第二个段落原来的代码:
Section { RatingView(rating: $rating) TextField("Write a review", text: $review) }复制代码
所需的代码就这些 —— 我们的默认值是合理的,所以视觉效果看起来很不错。你也不用再点击进入选择器视图中取选择星级,这些星星评级视图看起来更自然,也更常用。