扬庆の博客

五星好评的评论控件

字数统计: 729阅读时长: 4 min
2021/09/28 Share

五星评论控件

1 - 5 颗星, 用户可以随意点击。

素材

两张图, 一个是空心,表示没有选中; 一个是实心,表示选中。

高级控件

UICollectionView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

class RatingStarView: UIView, UICollectionViewDelegateFlowLayout {

var ratingStarCallBack: ((Double) -> Void)?

lazy var collectionView: UICollectionView = {
let collectionV = UICollectionView(frame: self.bounds, collectionViewLayout: ratingFlowlayout)
collectionV.translatesAutoresizingMaskIntoConstraints = false
collectionV.delegate = self
collectionV.dataSource = self
collectionV.register(RatingStarCollectionCell.self)
collectionV.isScrollEnabled = false
collectionV.isUserInteractionEnabled = false
collectionV.backgroundColor = .white
return collectionV
}()

lazy var ratingFlowlayout: UICollectionViewFlowLayout = {
let layout = UICollectionViewFlowLayout()
layout.minimumLineSpacing = 0
layout.minimumInteritemSpacing = 0
return layout
}()

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

public var selectCount: Double = 0 {
didSet {
collectionView.reloadData()
}
}

init() {
super.init(frame: .zero)
addSubview(collectionView)

NSLayoutConstraint.activate([
collectionView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
collectionView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
collectionView.topAnchor.constraint(equalTo: self.topAnchor),
collectionView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}

override func layoutSubviews() {
super.layoutSubviews()
collectionView.reloadData()
}
}

extension RatingStarView: UICollectionViewDataSource {

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {

let itemW: CGFloat = collectionView.frame.size.width / 5.0
let itemH: CGFloat = collectionView.frame.size.height
return CGSize(width: itemW, height: itemH)
}

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return 5
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(RatingStarCollectionCell.self, for: indexPath)

let offset = round(selectCount * 2.0) / 2.0
if Double(indexPath.row) < floor(offset) {
cell.selectImageView.image = R.image.rateStarFilled
} else {
cell.selectImageView.image = R.image.rateStarOutline
}
return cell
}
}

extension RatingStarView {

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first)
}

func handleTouch(_ touch: UITouch?) {

let touchPoint = touch?.location(in: collectionView)
let scale = touchPoint!.x / collectionView.frame.size.width

var offset = scale * 5
offset = ceil(offset)
offset = offset > 0 ? offset : 1 // default: one star
offset = offset <= 5 ? offset : 5

self.selectCount = Double(Int(offset))

ratingStarCallBack?(self.selectCount)
}

}

// MARK: - RatingStar Cell
class RatingStarCollectionCell: UICollectionViewCell, YReusable {

lazy var selectImageView: UIImageView = {
let imgView = UIImageView()
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.contentMode = .scaleAspectFit

return imgView
}()

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

override init(frame: CGRect) {
super.init(frame: frame)

contentView.addSubview(selectImageView)
NSLayoutConstraint.activate([
selectImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
selectImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
selectImageView.topAnchor.constraint(equalTo: contentView.topAnchor),
selectImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor)
])
}
}


以上就是全部代码, 下面分析代码。

重点

重点就是拿到手势触摸的 ( 开始 移动 结束 ) 这三个状态;

拿到point.x 除以最大宽度( 也就是 5 个 item 的宽度 ) , 通过占比计算出处在第几个item, 这样就能确定点击的 item index 了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first)
}

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first)
}

override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches.first)
}

func handleTouch(_ touch: UITouch?) {

let touchPoint = touch?.location(in: collectionView)
let scale = touchPoint!.x / collectionView.frame.size.width

var offset = scale * 5
offset = ceil(offset)
offset = offset > 0 ? offset : 1 // default: one star
offset = offset <= 5 ? offset : 5

self.selectCount = Double(Int(offset))

ratingStarCallBack?(self.selectCount)
}

cell 初始化的时候通过触摸拿到的 index 选择显示的图片素材是哪个即可

1
2
3
4
5
6
7
8
let offset = round(selectCount * 2.0) / 2.0

if Double(indexPath.row) < floor(offset) {

cell.selectImageView.image = R.image.rateStarFilled
} else {
cell.selectImageView.image = R.image.rateStarOutline
}

看下效果图

五星好评

CATALOG
  1. 1. 五星评论控件
    1. 1.1. 重点