前言:
突然需要使用星星評(píng)分控件了。我們使用Swift開發(fā),閑的就改寫了一個(gè)庫狞玛。
Objective-C
原作者地址: https://github.com/akiroom/AXRatingView
Swift
代碼地址:https://github.com/gityuency/Autolayout
示例代碼類名 【RatingStarsViewController】 【SlidingRatingView】
所有支持的效果都在這個(gè)gif圖片里面了。
這次的改寫學(xué)到了一個(gè)騷東西,就是自己寫的View可以在XIB里面設(shè)置可編輯的屬性。
效果圖:
這個(gè)控件的部分屬性值設(shè)置支持XIB設(shè)置
源代碼,復(fù)制粘貼就能用
//
// SlidingRatingView.swift
// 姬友大人
//
// Created by 姬友大人 on 2019/5/20.
// Copyright ? 2019 FunPlus. All rights reserved.
//
import Foundation
import UIKit
@IBDesignable
class SlidingRatingView: UIControl {
private var starMaskLayer: CALayer?
///正常狀態(tài)
private var basementLayer: CALayer?
///高亮狀態(tài)
private var highlightLayer: CALayer?
///計(jì)算得到文字大小
private var cachedMarkCharacterSize = CGSize.zero;
/// 星星個(gè)數(shù)的浮點(diǎn)型數(shù)據(jù),用于計(jì)算,省的每次都要轉(zhuǎn)換
private var numberOfStar_float: CGFloat = 5
// 設(shè)置星星的個(gè)數(shù)
@IBInspectable var numberOfStar: Int = 5 {
didSet {
if oldValue != numberOfStar {
numberOfStar_float = CGFloat(numberOfStar)
self.invalidateIntrinsicContentSize()
self.setNeedsDisplay()
}
}
}
//設(shè)置默認(rèn)的字符
@IBInspectable var markCharacter: String = "\u{2605}" {
didSet {
if oldValue != markCharacter {
markImage = nil
cachedMarkCharacterSize = CGSize.zero
self.invalidateIntrinsicContentSize()
self.setNeedsDisplay()
}
}
}
///設(shè)置默認(rèn)的字體
@IBInspectable var markFont: UIFont = UIFont.systemFont(ofSize: 22) {
didSet {
if oldValue != markFont {
markImage = nil;
setNeedsDisplay()
}
}
}
///如果有圖片,優(yōu)先使用圖片, 如果沒有圖片就使用文字
@IBInspectable var markImage: UIImage?
///正常顏色
@IBInspectable var baseColor: UIColor = UIColor.lightGray {
didSet {
if oldValue != baseColor {
basementLayer?.removeFromSuperlayer();
basementLayer = nil
setNeedsDisplay();
}
}
}
///高亮顏色
@IBInspectable var highlightColor: UIColor = UIColor.red {
didSet {
if oldValue != highlightColor {
highlightLayer?.removeFromSuperlayer()
starMaskLayer?.removeFromSuperlayer()
highlightLayer = nil
starMaskLayer = nil
setNeedsDisplay()
}
}
}
///當(dāng)前的值
@IBInspectable var value: Float = 0.0 {
didSet {
if oldValue != value {
value = min(max(value, 0.0), Float(numberOfStar_float))
setNeedsDisplay()
}
}
}
///步長(zhǎng)
@IBInspectable var stepInterval: CGFloat = 0.0 {
didSet {
stepInterval = max(stepInterval, 0.0)
}
}
///最小值
@IBInspectable var minimumValue: CGFloat = 0.0
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func sizeToFit() {
super.sizeToFit()
self.frame = CGRect(origin: self.frame.origin, size: self.intrinsicContentSize)
}
/// 這個(gè)是什么,沒有見過
override func systemLayoutSizeFitting(_ targetSize: CGSize, withHorizontalFittingPriority horizontalFittingPriority: UILayoutPriority, verticalFittingPriority: UILayoutPriority) -> CGSize {
return self.intrinsicContentSize;
}
override var intrinsicContentSize: CGSize {
var size = CGSize.zero
if let markImage = markImage {
size = markImage.size
} else {
size = needMarkCharacterSize()
}
return CGSize(width: size.width * numberOfStar_float, height: size.height);
}
override func draw(_ rect: CGRect) {
if starMaskLayer == nil {
starMaskLayer = needMaskLayer();
layer.mask = starMaskLayer;
basementLayer = needBasementLayer();
layer.addSublayer(basementLayer!);
highlightLayer = needHighlightLayer()
layer.addSublayer(highlightLayer!);
}
let selfWidth = markImage!.size.width * numberOfStar_float
let selfHalfWidth = selfWidth / 2
let selfHalfHeight = frame.size.height / 2
let frameOffsetX = (frame.size.width - selfWidth) / 2;
let offsetX = selfWidth / numberOfStar_float * (numberOfStar_float - CGFloat(value))
CATransaction.begin()
CATransaction.setValue(true, forKey: kCATransactionDisableActions)
highlightLayer?.position = CGPoint(x: selfHalfWidth - (offsetX - frameOffsetX), y: selfHalfHeight)
CATransaction.commit()
}
/// 這個(gè)方法返回字符的尺寸
func needMarkCharacterSize() -> CGSize {
if cachedMarkCharacterSize.equalTo(CGSize.zero) { //如果這個(gè)緩存之前沒有存下數(shù)據(jù), 是空的, 就重新計(jì)算
let size: CGSize = markCharacter.size(withAttributes: [NSAttributedString.Key.font: markFont])
cachedMarkCharacterSize = size;
}
return cachedMarkCharacterSize
}
/// 返回圖片
func needMarkImage() -> UIImage {
if let markImage = markImage { //如果有圖片,就直接返回圖片
return markImage
} else { //如果沒有圖片,就返回文字生成的圖片
let size = self.needMarkCharacterSize()
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
UIColor.black.set()
let text: NSString = NSString(cString: markCharacter.cString(using: .utf8)!, encoding: String.Encoding.utf8.rawValue)!
text.draw(at: CGPoint.zero, withAttributes: [NSAttributedString.Key.font: markFont, NSAttributedString.Key.foregroundColor: UIColor.black]);
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
markImage = image
return markImage!
}
}
/// 生成 mask layer
func needMaskLayer() -> CALayer {
// 這里是按照p圖片生成的 image, 一定是有東西的
markImage = self.needMarkImage();
let starMaskLayer = CALayer();
starMaskLayer.isOpaque = false;
if let markimage = markImage {
let markWidth = markimage.size.width;
let markHalfWidth = markWidth / 2
let markHeight = markimage.size.height
let markHalfHeight = markHeight / 2;
for i in 0..<numberOfStar {
let starLayer = CALayer()
starLayer.contents = markimage.cgImage
starLayer.bounds = CGRect(origin: CGPoint.zero, size: markimage.size)
starLayer.position = CGPoint(x: markHalfWidth + markWidth * CGFloat(i), y: markHalfHeight)
starMaskLayer.addSublayer(starLayer)
}
let totalStartsWidth = (markWidth * numberOfStar_float);
let frameOffsetX = (self.frame.size.width - totalStartsWidth) / 2
let frameOffsetY = (self.frame.size.height - markimage.size.height) / 2
starMaskLayer.frame = CGRect(x: frameOffsetX, y: frameOffsetY, width: markimage.size.width * numberOfStar_float, height: markimage.size.height)
}
return starMaskLayer;
}
/// 設(shè)置 Basement Layer
func needBasementLayer() -> CALayer {
let layer = CALayer()
layer.backgroundColor = baseColor.cgColor
if let markimage = markImage {
layer.bounds = CGRect(origin: CGPoint.zero, size: CGSize(width: markimage.size.width * numberOfStar_float, height: markimage.size.height))
layer.position = CGPoint(x: bounds.midX, y: bounds.midY)
}
return layer;
}
/// 生成高亮laier
func needHighlightLayer() -> CALayer {
let layer = CALayer()
layer.backgroundColor = highlightColor.cgColor
if let markimage = markImage {
layer.bounds = CGRect(origin: CGPoint.zero, size: CGSize(width: markimage.size.width * numberOfStar_float, height: markimage.size.height))
layer.position = CGPoint(x: bounds.midX, y: bounds.midY)
}
return layer;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
touchesMoved(touches, with: event)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let location = touches.first?.location(in: self)
if let markimage = markImage {
let totalStartsWidth = markimage.size.width * numberOfStar_float
let frameOffsetX = (frame.size.width - totalStartsWidth ) / 2
let frameOffsetY = (frame.size.height - markimage.size.height) / 2
let rect = CGRect(x: frameOffsetX - markimage.size.width, y: frameOffsetY, width: totalStartsWidth + markimage.size.width, height: markimage.size.height)
if rect.contains(location!) {
var value = ((location!.x - frameOffsetX) / (markimage.size.width * numberOfStar_float) * numberOfStar_float);
if stepInterval != 0 {
value = max(minimumValue, ceil(value / stepInterval) * stepInterval)
} else {
value = max(minimumValue, value)
}
self.value = Float(value)
self .sendActions(for: UIControl.Event.valueChanged)
}
}
}
}