美文网首页
UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)

UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)

作者: 刀客传奇 | 来源:发表于2018-11-19 15:27 被阅读108次

    版本记录

    版本号 时间
    V1.0 2018.11.19 星期一

    前言

    iOS中有关视图控件用户能看到的都在UIKit框架里面,用户交互也是通过UIKit进行的。感兴趣的参考上面几篇文章。
    1. UIKit框架(一) —— UIKit动力学和移动效果(一)
    2. UIKit框架(二) —— UIKit动力学和移动效果(二)
    3. UIKit框架(三) —— UICollectionViewCell的扩张效果的实现(一)
    4. UIKit框架(四) —— UICollectionViewCell的扩张效果的实现(二)
    5. UIKit框架(五) —— 自定义控件:可重复使用的滑块(一)

    源码

    1. Swift

    下面看一下源码

    1. ViewController.swift
    
    import UIKit
    
    class ViewController: UIViewController {
      let rangeSlider = RangeSlider(frame: .zero)
      
      override func viewDidLoad() {
        super.viewDidLoad()
        
        view.addSubview(rangeSlider)
        rangeSlider.addTarget(self, action: #selector(rangeSliderValueChanged(_:)),
                              for: .valueChanged)
        let time = DispatchTime.now() + 1
        DispatchQueue.main.asyncAfter(deadline: time) {
          self.rangeSlider.trackHighlightTintColor = .red
          self.rangeSlider.thumbImage = #imageLiteral(resourceName: "RectThumb")
          self.rangeSlider.highlightedThumbImage = #imageLiteral(resourceName: "HighlightedRect")
        }
      }
      
      override func viewDidLayoutSubviews() {
        let margin: CGFloat = 20
        let width = view.bounds.width - 2 * margin
        let height: CGFloat = 30
        
        rangeSlider.frame = CGRect(x: 0, y: 0,
                                   width: width, height: height)
        rangeSlider.center = view.center
      }
      
      @objc func rangeSliderValueChanged(_ rangeSlider: RangeSlider) {
        let values = "(\(rangeSlider.lowerValue) \(rangeSlider.upperValue))"
        print("Range slider value changed: \(values)")
      }
    }
    
    2. RangeSliderTrackLayer.swift
    
    import UIKit
    
    class RangeSliderTrackLayer: CALayer {
      weak var rangeSlider: RangeSlider?
      
      override func draw(in ctx: CGContext) {
        guard let slider = rangeSlider else {
          return
        }
        
        let path = UIBezierPath(roundedRect: bounds, cornerRadius: cornerRadius)
        ctx.addPath(path.cgPath)
        
        ctx.setFillColor(slider.trackTintColor.cgColor)
        ctx.fillPath()
        
        ctx.setFillColor(slider.trackHighlightTintColor.cgColor)
        let lowerValuePosition = slider.positionForValue(slider.lowerValue)
        let upperValuePosition = slider.positionForValue(slider.upperValue)
        let rect = CGRect(x: lowerValuePosition, y: 0,
                          width: upperValuePosition - lowerValuePosition,
                          height: bounds.height)
        ctx.fill(rect)
      }
    }
    
    3. RangeSlider.swift
    
    import UIKit
    
    class RangeSlider: UIControl {
      override var frame: CGRect {
        didSet {
          updateLayerFrames()
        }
      }
      
      var minimumValue: CGFloat = 0 {
        didSet {
          updateLayerFrames()
        }
      }
      
      var maximumValue: CGFloat = 1 {
        didSet {
          updateLayerFrames()
        }
      }
      
      var lowerValue: CGFloat = 0.2 {
        didSet {
          updateLayerFrames()
        }
      }
      
      var upperValue: CGFloat = 0.8 {
        didSet {
          updateLayerFrames()
        }
      }
      
      var trackTintColor = UIColor(white: 0.9, alpha: 1) {
        didSet {
          trackLayer.setNeedsDisplay()
        }
      }
      
      var trackHighlightTintColor = UIColor(red: 0, green: 0.45, blue: 0.94, alpha: 1) {
        didSet {
          trackLayer.setNeedsDisplay()
        }
      }
      
      var thumbImage = #imageLiteral(resourceName: "Oval") {
        didSet {
          upperThumbImageView.image = thumbImage
          lowerThumbImageView.image = thumbImage
          updateLayerFrames()
        }
      }
      
      var highlightedThumbImage = #imageLiteral(resourceName: "HighlightedOval") {
        didSet {
          upperThumbImageView.highlightedImage = highlightedThumbImage
          lowerThumbImageView.highlightedImage = highlightedThumbImage
          updateLayerFrames()
        }
      }
      
      
      private let trackLayer = RangeSliderTrackLayer()
      private let lowerThumbImageView = UIImageView()
      private let upperThumbImageView = UIImageView()
      private var previousLocation = CGPoint()
      
      override init(frame: CGRect) {
        super.init(frame: frame)
        
        trackLayer.rangeSlider = self
        trackLayer.contentsScale = UIScreen.main.scale
        layer.addSublayer(trackLayer)
        
        lowerThumbImageView.image = thumbImage
        addSubview(lowerThumbImageView)
        
        upperThumbImageView.image = thumbImage
        addSubview(upperThumbImageView)
      }
    
      
      required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
      }
      
      // 1
      private func updateLayerFrames() {
        CATransaction.begin()
        CATransaction.setDisableActions(true)
    
        trackLayer.frame = bounds.insetBy(dx: 0.0, dy: bounds.height / 3)
        trackLayer.setNeedsDisplay()
        lowerThumbImageView.frame = CGRect(origin: thumbOriginForValue(lowerValue),
                                           size: thumbImage.size)
        upperThumbImageView.frame = CGRect(origin: thumbOriginForValue(upperValue),
                                           size: thumbImage.size)
        CATransaction.commit()
      }
      // 2
      func positionForValue(_ value: CGFloat) -> CGFloat {
        return bounds.width * value
      }
      // 3
      private func thumbOriginForValue(_ value: CGFloat) -> CGPoint {
        let x = positionForValue(value) - thumbImage.size.width / 2.0
        return CGPoint(x: x, y: (bounds.height - thumbImage.size.height) / 2.0)
      }
    }
    
    extension RangeSlider {
      override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        // 1
        previousLocation = touch.location(in: self)
        
        // 2
        if lowerThumbImageView.frame.contains(previousLocation) {
          lowerThumbImageView.isHighlighted = true
        } else if upperThumbImageView.frame.contains(previousLocation) {
          upperThumbImageView.isHighlighted = true
        }
        
        // 3
        return lowerThumbImageView.isHighlighted || upperThumbImageView.isHighlighted
      }
      
      override func continueTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
        let location = touch.location(in: self)
        
        // 1
        let deltaLocation = location.x - previousLocation.x
        let deltaValue = (maximumValue - minimumValue) * deltaLocation / bounds.width
        
        previousLocation = location
        
        // 2
        if lowerThumbImageView.isHighlighted {
          lowerValue += deltaValue
          lowerValue = boundValue(lowerValue, toLowerValue: minimumValue,
                                  upperValue: upperValue)
        } else if upperThumbImageView.isHighlighted {
          upperValue += deltaValue
          upperValue = boundValue(upperValue, toLowerValue: lowerValue,
                                  upperValue: maximumValue)
        }
        
        sendActions(for: .valueChanged)
        return true
      }
      
      override func endTracking(_ touch: UITouch?, with event: UIEvent?) {
        lowerThumbImageView.isHighlighted = false
        upperThumbImageView.isHighlighted = false
      }
      
      // 4
      private func boundValue(_ value: CGFloat, toLowerValue lowerValue: CGFloat, upperValue: CGFloat) -> CGFloat {
        return min(max(value, lowerValue), upperValue)
      }
    }
    

    后记

    本篇主要讲述了自定义控件:可重复使用的滑块,感兴趣的给个赞或者关注~~~

    相关文章

      网友评论

          本文标题:UIKit框架(六) —— 自定义控件:可重复使用的滑块(二)

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