美文网首页IOS 学习
Swift 星星评分控件 改写

Swift 星星评分控件 改写

作者: Yuency | 来源:发表于2019-05-21 11:20 被阅读0次

    前言:

    突然需要使用星星评分控件了。我们使用Swift开发,闲的就改写了一个库。

    Objective-C
    原作者地址: https://github.com/akiroom/AXRatingView

    Swift
    代码地址:https://github.com/gityuency/Autolayout
    示例代码类名 【RatingStarsViewController】 【SlidingRatingView】

    所有支持的效果都在这个gif图片里面了。
    这次的改写学到了一个骚东西,就是自己写的View可以在XIB里面设置可编辑的属性。

    效果图:


    星星评分控件.gif

    这个控件的部分属性值设置支持XIB设置


    XIB编辑.gif

    源代码,复制粘贴就能用

    //
    //  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?
        ///正常状态
        private var basementLayer: CALayer?
        ///高亮状态
        private var highlightLayer: CALayer?
        
        ///计算得到文字大小
        private var cachedMarkCharacterSize = CGSize.zero;
        
        /// 星星个数的浮点型数据,用于计算,省的每次都要转换
        private var numberOfStar_float: CGFloat = 5
        
        // 设置星星的个数
        @IBInspectable var numberOfStar: Int = 5 {
            didSet {
                if oldValue != numberOfStar {
                    numberOfStar_float = CGFloat(numberOfStar)
                    self.invalidateIntrinsicContentSize()
                    self.setNeedsDisplay()
                }
            }
        }
        
        //设置默认的字符
        @IBInspectable var markCharacter: String = "\u{2605}" {
            didSet {
                if oldValue != markCharacter {
                    markImage = nil
                    cachedMarkCharacterSize = CGSize.zero
                    self.invalidateIntrinsicContentSize()
                    self.setNeedsDisplay()
                }
            }
        }
        
        ///设置默认的字体
        @IBInspectable var markFont: UIFont = UIFont.systemFont(ofSize: 22) {
            didSet {
                if oldValue != markFont {
                    markImage = nil;
                    setNeedsDisplay()
                }
            }
        }
        
        
        ///如果有图片,优先使用图片, 如果没有图片就使用文字
        @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()
                }
            }
        }
        
        ///当前的值
        @IBInspectable var value: Float = 0.0 {
            didSet {
                if oldValue != value {
                    value = min(max(value, 0.0), Float(numberOfStar_float))
                    setNeedsDisplay()
                }
            }
        }
        
        ///步长
        @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)
        }
        
        /// 这个是什么,没有见过
        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()
        }
        
        /// 这个方法返回字符的尺寸
        func needMarkCharacterSize() -> CGSize {
            if cachedMarkCharacterSize.equalTo(CGSize.zero) {  //如果这个缓存之前没有存下数据, 是空的, 就重新计算
                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;
        }
        
        /// 设置  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)
                }
            }
        }
    }
    
    

    相关文章

      网友评论

        本文标题:Swift 星星评分控件 改写

        本文链接:https://www.haomeiwen.com/subject/dupdzqtx.html