像youtube应用程序一样快速移动UISlider

时间:2015-08-11 15:34:30

标签: ios swift uislider

我可以创建一个具有缓冲进度的自定义UISlider,就像Youtube应用程序中的那样吗?我知道这个问题可能是我可能重复的;但没有人能正确解决iOS或Swift中的问题

谢谢

2 个答案:

答案 0 :(得分:8)

您需要子类UIControl并在drawRect:中绘制三个图层 - 一个用于背景线,第二个用于缓冲线,第三个用于进度,具体取决于当前进度和可用缓冲区值。例如:

class MySlider: UIControl
{
    var currentPosition : Float = 0.0
    {
        didSet
        {
            updateLayers()
        }
    }

    var currentBuffer : Float = 0.0
    {
        didSet
        {
            updateLayers()
        }
    }

    var backgroundLayerColor : UIColor = UIColor.lightGrayColor()
    var progressLayerColor : UIColor = UIColor.redColor()
    var bufferLayerColor : UIColor = UIColor.darkGrayColor()
    var positionRingLayerColor : UIColor = UIColor.redColor()

    private var backgroundLayer : CAShapeLayer!
    private var progressLayer : CAShapeLayer!
    private var bufferLayer : CAShapeLayer!
    private var positionRingLayer : CAShapeLayer!

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

    required init(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
        initialize()
    }

    override func drawRect(rect: CGRect)
    {
        updateLayers()
    }

    private func initialize()
    {
        self.backgroundColor = UIColor.clearColor()

        backgroundLayer = CAShapeLayer()
        backgroundLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
        backgroundLayer.path = UIBezierPath(rect: CGRect(x: 0, y: (self.frame.size.height / 2) - self.frame.size.height / 4, width: self.frame.size.width, height: self.frame.size.height / 2.0)).CGPath
        backgroundLayer.fillColor = backgroundLayerColor.CGColor
        backgroundLayer.backgroundColor = UIColor.clearColor().CGColor

        progressLayer = CAShapeLayer()
        progressLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)

        bufferLayer = CAShapeLayer()
        bufferLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)

        positionRingLayer = CAShapeLayer()
        positionRingLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)

        self.layer.addSublayer(backgroundLayer)
        self.layer.addSublayer(bufferLayer)
        self.layer.addSublayer(progressLayer)
        self.layer.addSublayer(positionRingLayer)

        updateLayers()
    }

    private func updateLayers()
    {
        updateProgressLine()
        updateBufferLine()
        updatePositionRing()
    }

    private func updateProgressLine()
    {
        var w = (self.frame.size.width * CGFloat(currentPosition)) + self.frame.size.height / 4

        if w > self.frame.size.width
        {
            w = self.frame.size.width
        }

        progressLayer.path = UIBezierPath(rect: CGRect(x: 0, y: (self.frame.size.height / 2) - self.frame.size.height / 4, width: w, height: self.frame.size.height / 2)).CGPath
        progressLayer.fillColor = progressLayerColor.CGColor
        progressLayer.backgroundColor = UIColor.clearColor().CGColor
    }

    private func updateBufferLine()
    {
        var w = self.frame.size.width * CGFloat(currentBuffer)

        bufferLayer.path = UIBezierPath(rect: CGRect(x: 0, y: (self.frame.size.height / 2) - self.frame.size.height / 4, width: w, height: self.frame.size.height / 2)).CGPath
        bufferLayer.fillColor = bufferLayerColor.CGColor
        bufferLayer.backgroundColor = UIColor.clearColor().CGColor
    }

    private func updatePositionRing()
    {
        var _x = self.frame.size.width * CGFloat(currentPosition)

        if _x > self.frame.size.width - self.frame.size.height
        {
            _x = self.frame.size.width - self.frame.size.height
        }

        positionRingLayer.path = UIBezierPath(ovalInRect: CGRect(x: _x, y: 0, width: self.frame.size.height, height: self.frame.size.height)).CGPath
        positionRingLayer.fillColor = positionRingLayerColor.CGColor
        positionRingLayer.backgroundColor = UIColor.clearColor().CGColor
    }

    override func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool
    {
        super.continueTrackingWithTouch(touch, withEvent: event)
        let point = touch.locationInView(self)

        var _xb = (self.frame.size.width * CGFloat(currentBuffer)) - self.frame.size.height
        if (point.x < _xb) && (point.x > 0)
        {
            currentPosition = Float(point.x / self.frame.size.width)
            self.setNeedsDisplay()
        }
        return true
    }
}

在某些视图控制器中:

let slider = MySlider(frame: CGRect(x: 50, y: 100, width: 200, height: 30))
self.view.addSubview(slider)
slider.currentPosition = 0.3
slider.currentBuffer = 0.7

结果:

custom slider

答案 1 :(得分:1)

有效! 我添加了一个协议来处理组件的滚动,使其行为类似于UISlider

public protocol SliderDelegate {
    func SliderBeginDragging(slider: bufferdSlider)
    func SliderEndDragging(slider: bufferdSlider)
    func SliderScrub(slider: bufferdSlider)
}

public enum ScrollState: Int, Printable {
case BeginDragging = 0
case EndDragging
case scrub
    public var description: String {
        get {
            switch self {
            case BeginDragging:
                return "BeginDragging"
            case EndDragging:
                return "EndDragging"
            case scrub:
                return "Scrub"
            }
        }
    }
}

public class  bufferdSlider: UIControl
{
    public var sliderDelegate: SliderDelegate!
    var currentPosition : Float = 0.0
    {
        didSet
        {
            updateLayers()
        }
    }
    var currentBuffer : Float = 0.0
    {
        didSet
        {
            updateLayers()
        }
    }
    var backgroundLayerColor : UIColor = UIColor.lightGrayColor()
    var progressLayerColor : UIColor = UIColor.redColor()
    var bufferLayerColor : UIColor = UIColor.darkGrayColor()
    var positionRingLayerColor : UIColor = UIColor.redColor()
    private var backgroundLayer : CAShapeLayer!
    private var progressLayer : CAShapeLayer!
    private var bufferLayer : CAShapeLayer!
    private var positionRingLayer : CAShapeLayer!


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

    required public init(coder aDecoder: NSCoder)
    {
        super.init(coder: aDecoder)
        initialize()
    }

    override public func drawRect(rect: CGRect)
    {
        updateLayers()
    }

    private func initialize()
    {
        self.backgroundColor = UIColor.clearColor()

        backgroundLayer = CAShapeLayer()
        backgroundLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
        backgroundLayer.path = UIBezierPath(rect: CGRect(x: 0, y: (self.frame.size.height / 2) - self.frame.size.height / 4, width: self.frame.size.width, height: self.frame.size.height / 2.0)).CGPath
        backgroundLayer.fillColor = backgroundLayerColor.CGColor
        backgroundLayer.backgroundColor = UIColor.clearColor().CGColor
        progressLayer = CAShapeLayer()
        progressLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
        bufferLayer = CAShapeLayer()
        bufferLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
        positionRingLayer = CAShapeLayer()
        positionRingLayer.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
        self.layer.addSublayer(backgroundLayer)
        self.layer.addSublayer(bufferLayer)
        self.layer.addSublayer(progressLayer)
        self.layer.addSublayer(positionRingLayer)
        updateLayers()
    }

    private func updateLayers()
    {
        updateProgressLine()
        updateBufferLine()
        updatePositionRing()
    }

    private func updateProgressLine()
    {
        var w = (self.frame.size.width * CGFloat(currentPosition)) + self.frame.size.height / 4

        if w > self.frame.size.width
        {
            w = self.frame.size.width
        }

        progressLayer.path = UIBezierPath(rect: CGRect(x: 0, y: (self.frame.size.height / 2) - self.frame.size.height / 4, width: w, height: self.frame.size.height / 2)).CGPath
        progressLayer.fillColor = progressLayerColor.CGColor
        progressLayer.backgroundColor = UIColor.clearColor().CGColor
    }

    private func updateBufferLine()
    {
        var w = self.frame.size.width * CGFloat(currentBuffer)

        bufferLayer.path = UIBezierPath(rect: CGRect(x: 0, y: (self.frame.size.height / 2) - self.frame.size.height / 4, width: w, height: self.frame.size.height / 2)).CGPath
        bufferLayer.fillColor = bufferLayerColor.CGColor
        bufferLayer.backgroundColor = UIColor.clearColor().CGColor
    }

    private func updatePositionRing()
    {
        var _x = self.frame.size.width * CGFloat(currentPosition)

        if _x > self.frame.size.width - self.frame.size.height
        {
            _x = self.frame.size.width - self.frame.size.height
        }

        positionRingLayer.path = UIBezierPath(ovalInRect: CGRect(x: _x, y: 0, width: self.frame.size.height, height: self.frame.size.height)).CGPath
        positionRingLayer.fillColor = positionRingLayerColor.CGColor
        positionRingLayer.backgroundColor = UIColor.clearColor().CGColor
    }

    override public func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool {
        super.beginTrackingWithTouch(touch, withEvent: event)
        println("beginTrackingWithTouch")
        sliderDelegate.SliderBeginDragging(self)
        return true
    }


    override public func continueTrackingWithTouch(touch: UITouch, withEvent event: UIEvent) -> Bool
    {
        super.continueTrackingWithTouch(touch, withEvent: event)

        sliderDelegate.SliderScrub(self)
        let point = touch.locationInView(self)

        var _xb = (self.frame.size.width * CGFloat(currentBuffer)) - self.frame.size.height
        if  (point.x > 0) // if (point.x < _xb) && (point.x > 0) used for seek and play
        {
            currentPosition = Float(point.x / self.frame.size.width)
            self.setNeedsDisplay()
        }
        return true
    }
    override public func endTrackingWithTouch(touch: UITouch, withEvent event: UIEvent){
        super.endTrackingWithTouch(touch, withEvent: event)
        println("endTrackingWithTouch")
        sliderDelegate.SliderEndDragging(self)
    }

    override public func cancelTrackingWithEvent(event: UIEvent?) {
        super.cancelTrackingWithEvent(event)
        println("cancelTrackingWithEvent")
        sliderDelegate.SliderEndDragging(self)
    }
}