在 UIImageView 上添加部分遮罩
Adding a partial mask over an UIImageView
我想在图像的一部分上添加 0.5 alpha 掩码(我将在代码中计算)。
基本上,它是一个 5 星评级控件,但星星不是一种颜色,而是一些像这样的漂亮图片:
我需要尊重图片的透明背景。因此,我希望能够添加遮罩或以某种方式设置图像的一半的 alpha,例如,当您的评级为 3.5 时。 (2 颗满星和一颗有一半的 alpha 较少)
我不能只在上面放一个 0.5 alpha 的 UIView,因为这也会影响显示星星的背景。
有什么想法吗?
您可以使用 CAGradientLayer
作为掩码:
gLayer.startPoint = CGPoint.zero
gLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
gLayer.locations = [
0.0, 0.5, 0.5, 1.0,
]
gLayer.colors = [
UIColor.black.cgColor,
UIColor.black.cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor,
]
这将创建一个水平渐变,左半部分为全 alpha,右半部分为 50% alpha。
因此,以此作为掩码的白色视图将如下所示:
如果我们将图片设置为您的明星,它看起来像这样:
如果我们希望星星“填充 75%”,我们更改位置:
gLayer.locations = [
0.0, 0.75, 0.75, 1.0,
]
导致:
这是“五星级”评级视图的示例实现:
@IBDesignable
class FiveStarRatingView: UIView {
@IBInspectable
public var rating: CGFloat = 0.0 {
didSet {
var r = rating
stack.arrangedSubviews.forEach {
if let v = [=12=] as? PercentImageView {
v.percent = min(1.0, r)
r -= 1.0
}
}
}
}
@IBInspectable
public var ratingImage: UIImage = UIImage() {
didSet {
stack.arrangedSubviews.forEach {
if let v = [=12=] as? PercentImageView {
v.image = ratingImage
}
}
}
}
@IBInspectable
public var tranparency: CGFloat = 0.5 {
didSet {
stack.arrangedSubviews.forEach {
if let v = [=12=] as? PercentImageView {
v.tranparency = tranparency
}
}
}
}
override var intrinsicContentSize: CGSize {
return CGSize(width: 100.0, height: 20.0)
}
private let stack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.alignment = .center
v.distribution = .fillEqually
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
addSubview(stack)
// constrain stack view to all 4 sides
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: topAnchor),
stack.leadingAnchor.constraint(equalTo: leadingAnchor),
stack.trailingAnchor.constraint(equalTo: trailingAnchor),
stack.bottomAnchor.constraint(equalTo: bottomAnchor),
])
// add 5 Percent Image Views to the stack view
for _ in 1...5 {
let v = PercentImageView(frame: .zero)
stack.addArrangedSubview(v)
v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
}
}
private class PercentImageView: UIImageView {
var percent: CGFloat = 0.0 {
didSet {
setNeedsLayout()
}
}
var tranparency: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}
private let gLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gLayer.startPoint = CGPoint.zero
gLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
layer.mask = gLayer
}
override func layoutSubviews() {
super.layoutSubviews()
// we don't want the layer's intrinsic animation
CATransaction.begin()
CATransaction.setDisableActions(true)
gLayer.frame = bounds
gLayer.locations = [
0.0, percent as NSNumber, percent as NSNumber, 1.0,
]
gLayer.colors = [
UIColor.black.cgColor,
UIColor.black.cgColor,
UIColor.black.withAlphaComponent(tranparency).cgColor,
UIColor.black.withAlphaComponent(tranparency).cgColor,
]
CATransaction.commit()
}
}
}
class StarRatingViewController: UIViewController {
let ratingView = FiveStarRatingView()
let slider = UISlider()
let valueLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
guard let starImage = UIImage(named: "star") else {
fatalError("Could not load image named \"star\"")
}
// add a slider and a couple labels so we can change the rating
let minLabel = UILabel()
let maxLabel = UILabel()
[slider, valueLabel, minLabel, maxLabel].forEach {
view.addSubview([=12=])
[=12=].translatesAutoresizingMaskIntoConstraints = false
if let v = [=12=] as? UILabel {
v.textAlignment = .center
}
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
valueLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
valueLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
slider.topAnchor.constraint(equalTo: valueLabel.bottomAnchor, constant: 8.0),
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 32.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -32.0),
minLabel.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 8.0),
minLabel.centerXAnchor.constraint(equalTo: slider.leadingAnchor, constant: 0.0),
maxLabel.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 8.0),
maxLabel.centerXAnchor.constraint(equalTo: slider.trailingAnchor, constant: 0.0),
])
minLabel.text = "0"
maxLabel.text = "5"
ratingView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(ratingView)
NSLayoutConstraint.activate([
// constrain the rating view centered in the view
// 300-pts wide
// height will be auto-set by the rating view
ratingView.topAnchor.constraint(equalTo: minLabel.bottomAnchor, constant: 20.0),
ratingView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
ratingView.widthAnchor.constraint(equalToConstant: 240.0),
])
// use the star image
ratingView.ratingImage = starImage
// start at rating of 0 stars
updateValue(0.0)
slider.value = 0
slider.addTarget(self, action: #selector(self.sliderChanged(_:)), for: .valueChanged)
}
@objc func sliderChanged(_ sender: UISlider) {
// round the slider value to 2 decimal places
updateValue((sender.value * 5.0).rounded(digits: 2))
}
func updateValue(_ v: Float) -> Void {
valueLabel.text = String(format: "%.2f", v)
ratingView.rating = CGFloat(v)
}
}
extension Float {
func rounded(digits: Int) -> Float {
let multiplier = Float(pow(10.0, Double(digits)))
return (self * multiplier).rounded() / multiplier
}
}
结果:
请注意,FiveStarRatingView
class 已标记为 @IBDesignable
,因此您可以将其添加到 Storyboard / IB 中,并在设计时设置图像、透明度和等级。
我想在图像的一部分上添加 0.5 alpha 掩码(我将在代码中计算)。 基本上,它是一个 5 星评级控件,但星星不是一种颜色,而是一些像这样的漂亮图片:
我需要尊重图片的透明背景。因此,我希望能够添加遮罩或以某种方式设置图像的一半的 alpha,例如,当您的评级为 3.5 时。 (2 颗满星和一颗有一半的 alpha 较少)
我不能只在上面放一个 0.5 alpha 的 UIView,因为这也会影响显示星星的背景。
有什么想法吗?
您可以使用 CAGradientLayer
作为掩码:
gLayer.startPoint = CGPoint.zero
gLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
gLayer.locations = [
0.0, 0.5, 0.5, 1.0,
]
gLayer.colors = [
UIColor.black.cgColor,
UIColor.black.cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor,
UIColor.black.withAlphaComponent(0.5).cgColor,
]
这将创建一个水平渐变,左半部分为全 alpha,右半部分为 50% alpha。
因此,以此作为掩码的白色视图将如下所示:
如果我们将图片设置为您的明星,它看起来像这样:
如果我们希望星星“填充 75%”,我们更改位置:
gLayer.locations = [
0.0, 0.75, 0.75, 1.0,
]
导致:
这是“五星级”评级视图的示例实现:
@IBDesignable
class FiveStarRatingView: UIView {
@IBInspectable
public var rating: CGFloat = 0.0 {
didSet {
var r = rating
stack.arrangedSubviews.forEach {
if let v = [=12=] as? PercentImageView {
v.percent = min(1.0, r)
r -= 1.0
}
}
}
}
@IBInspectable
public var ratingImage: UIImage = UIImage() {
didSet {
stack.arrangedSubviews.forEach {
if let v = [=12=] as? PercentImageView {
v.image = ratingImage
}
}
}
}
@IBInspectable
public var tranparency: CGFloat = 0.5 {
didSet {
stack.arrangedSubviews.forEach {
if let v = [=12=] as? PercentImageView {
v.tranparency = tranparency
}
}
}
}
override var intrinsicContentSize: CGSize {
return CGSize(width: 100.0, height: 20.0)
}
private let stack: UIStackView = {
let v = UIStackView()
v.axis = .horizontal
v.alignment = .center
v.distribution = .fillEqually
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() -> Void {
addSubview(stack)
// constrain stack view to all 4 sides
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: topAnchor),
stack.leadingAnchor.constraint(equalTo: leadingAnchor),
stack.trailingAnchor.constraint(equalTo: trailingAnchor),
stack.bottomAnchor.constraint(equalTo: bottomAnchor),
])
// add 5 Percent Image Views to the stack view
for _ in 1...5 {
let v = PercentImageView(frame: .zero)
stack.addArrangedSubview(v)
v.heightAnchor.constraint(equalTo: v.widthAnchor).isActive = true
}
}
private class PercentImageView: UIImageView {
var percent: CGFloat = 0.0 {
didSet {
setNeedsLayout()
}
}
var tranparency: CGFloat = 0.5 {
didSet {
setNeedsLayout()
}
}
private let gLayer = CAGradientLayer()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() -> Void {
gLayer.startPoint = CGPoint.zero
gLayer.endPoint = CGPoint(x: 1.0, y: 0.0)
layer.mask = gLayer
}
override func layoutSubviews() {
super.layoutSubviews()
// we don't want the layer's intrinsic animation
CATransaction.begin()
CATransaction.setDisableActions(true)
gLayer.frame = bounds
gLayer.locations = [
0.0, percent as NSNumber, percent as NSNumber, 1.0,
]
gLayer.colors = [
UIColor.black.cgColor,
UIColor.black.cgColor,
UIColor.black.withAlphaComponent(tranparency).cgColor,
UIColor.black.withAlphaComponent(tranparency).cgColor,
]
CATransaction.commit()
}
}
}
class StarRatingViewController: UIViewController {
let ratingView = FiveStarRatingView()
let slider = UISlider()
let valueLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
guard let starImage = UIImage(named: "star") else {
fatalError("Could not load image named \"star\"")
}
// add a slider and a couple labels so we can change the rating
let minLabel = UILabel()
let maxLabel = UILabel()
[slider, valueLabel, minLabel, maxLabel].forEach {
view.addSubview([=12=])
[=12=].translatesAutoresizingMaskIntoConstraints = false
if let v = [=12=] as? UILabel {
v.textAlignment = .center
}
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
valueLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
valueLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
slider.topAnchor.constraint(equalTo: valueLabel.bottomAnchor, constant: 8.0),
slider.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 32.0),
slider.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -32.0),
minLabel.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 8.0),
minLabel.centerXAnchor.constraint(equalTo: slider.leadingAnchor, constant: 0.0),
maxLabel.topAnchor.constraint(equalTo: slider.bottomAnchor, constant: 8.0),
maxLabel.centerXAnchor.constraint(equalTo: slider.trailingAnchor, constant: 0.0),
])
minLabel.text = "0"
maxLabel.text = "5"
ratingView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(ratingView)
NSLayoutConstraint.activate([
// constrain the rating view centered in the view
// 300-pts wide
// height will be auto-set by the rating view
ratingView.topAnchor.constraint(equalTo: minLabel.bottomAnchor, constant: 20.0),
ratingView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
ratingView.widthAnchor.constraint(equalToConstant: 240.0),
])
// use the star image
ratingView.ratingImage = starImage
// start at rating of 0 stars
updateValue(0.0)
slider.value = 0
slider.addTarget(self, action: #selector(self.sliderChanged(_:)), for: .valueChanged)
}
@objc func sliderChanged(_ sender: UISlider) {
// round the slider value to 2 decimal places
updateValue((sender.value * 5.0).rounded(digits: 2))
}
func updateValue(_ v: Float) -> Void {
valueLabel.text = String(format: "%.2f", v)
ratingView.rating = CGFloat(v)
}
}
extension Float {
func rounded(digits: Int) -> Float {
let multiplier = Float(pow(10.0, Double(digits)))
return (self * multiplier).rounded() / multiplier
}
}
结果:
请注意,FiveStarRatingView
class 已标记为 @IBDesignable
,因此您可以将其添加到 Storyboard / IB 中,并在设计时设置图像、透明度和等级。