2016-05-17 3 views
0

У меня есть UIView с ограничивающей окружности, что-то вроде этогоКак сделать растягивание круг влияния на КСН

enter image description here

Я хочу, чтобы сделать эффект, как растягивая его, увидеть следующее изображение

enter image description here

Он любит эффект pullToRefresh, есть много библиотек или opensources сделать pullToRefresh, но они всегда принадлежит Tableview. Я просто хочу сделать эффект растяжения независимо. У меня просто кружил UIView, и это растягивается, когда я тяну его

Как я могу сделать это

ответ

0

Существует уже контроль для вас: circle with strech

Помимо выше я дам попытаться этот подход также: Я думаю, вы можете иметь полное изображение со стрелками, скрытое белым видом над ним. По мере того, как пользователь поворачивается вниз или вверх, вы можете отображать/скрывать часть изображения.

Если пользователь не нажал, то отобразите неровное изображение круга, и если пользователь пропустил расстояние до конца, покажите полностью растянутое изображение.

0

Вам, скорее всего, потребуется выполнить индивидуальный рисунок. Просто растяжение представления приведет к тому, что будет сглаживание, и представление будет искажено.

Для этого вы должны подклассифицировать UIView и переопределить drawRect, чтобы сделать какой-то рисунок, который может реагировать на взаимодействие пользователя.

Я создал класс образца:

class CircleRefresh:UIView{ 
    var taperFactor:CGFloat = 0.00{ 
     didSet{ 
      self.setNeedsDisplay() 
     } 
    } 

    var heightFactor: CGFloat = 0.40 
     { 
     didSet{ 
      self.setNeedsDisplay() 
     } 
    } 

    var radius:CGFloat 
    { 
     return self.frame.size.height * 0.20 
    } 
    //Set this to add an arrow or another image to the top circle 
    var refreshImage:UIImage?{ 
     didSet{ 
      // 
      self.subviews.forEach{$0.removeFromSuperview()} 
      if let image = self.refreshImage{ 
       let imageView = UIImageView(frame: CGRect(x:self.frame.size.width * 0.5 - self.radius,y:0.0,width:self.radius * 2.0,height:self.radius * 2.0)) 
       imageView.image = image 
       imageView.contentMode = .ScaleAspectFit 
       imageView.layer.cornerRadius = imageView.frame.size.height * 0.5 
       imageView.layer.masksToBounds = true 
       self.addSubview(imageView) 
      } 
     } 
    } 

    override func drawRect(rect:CGRect) 
    { 
     UIColor.lightGrayColor().setFill() 

     let endCenter = CGPoint(x:self.frame.size.width * 0.5,y:self.frame.size.height * heightFactor - radius * taperFactor) 

     //top arc 
     let path = UIBezierPath(arcCenter: CGPoint(x:self.frame.size.width * 0.5, y:radius), radius: self.frame.size.height * 0.20, startAngle: 0.0, endAngle: CGFloat(M_PI), clockwise: false) 
     //left curve 
     path.addCurveToPoint(CGPoint(x:endCenter.x - radius * taperFactor,y:endCenter.y), controlPoint1: CGPoint(x:endCenter.x - radius, y:radius * 2.0), controlPoint2: CGPoint(x: endCenter.x - radius * taperFactor, y: radius * 2.0)) 
     //bottom arc 
     path.addArcWithCenter(endCenter, radius: radius * taperFactor, startAngle: CGFloat(M_PI), endAngle: 0.0, clockwise: false) 
     //right curve 
     path.addCurveToPoint(CGPoint(x:endCenter.x + radius,y:radius), controlPoint1: CGPoint(x: endCenter.x + radius * taperFactor, y: radius * 2.0),controlPoint2: CGPoint(x:endCenter.x + radius, y:radius * 2.0)) 

     path.fill() 
    } 
} 

Это будет нарисовать круг, но изменения taperFactor и heightFactor даст эффект растяжения. Установка heightFactor в 1.0 означает, что рисунок будет занимать всю высоту изображения, а установка taperFactor - 1.0 приведет к тому, что хвост чертежа будет таким же широким, как верхний круг.

Чтобы получить чувство для того, как меняется heightFactor и taperFactor меняет рисунок, вы можете добавить панорамирование жест распознаватель с этой точкой зрения, и в это действие что-то вроде:

@IBAction func pan(sender: UIPanGestureRecognizer) { 
     let location = sender.locationInView(self.view) 
     circle.heightFactor = location.y/self.view.frame.size.height 
     circle.taperFactor = circle.heightFactor * 0.5 - 0.20 
    } 

Вы можете изменить heightFactor и taperFactor для достижения различных эффектов. Это только часть того, как создать контроллер обновления, но, похоже, это вопрос вашего вопроса.

3

Основная идея - использовать траектории безье, чтобы очертить точную форму, которую вы ищете.Затем вы можете использовать жест, чтобы изменить эту форму и использовать ссылку дисплея, чтобы оживить возвращение растянутой окружности обратно в круговой форме:

@IBDesignable 
class RefreshView: UIView { 

    private let shapeLayer = CAShapeLayer() 

    @IBInspectable 
    var fillColor: UIColor = UIColor.lightGrayColor() { 
     didSet { 
      shapeLayer.fillColor = fillColor.CGColor 
     } 
    } 

    @IBInspectable 
    var strokeColor: UIColor = UIColor.clearColor() { 
     didSet { 
      shapeLayer.strokeColor = strokeColor.CGColor 
     } 
    } 

    @IBInspectable 
    var lineWidth: CGFloat = 0.0 { 
     didSet { 
      shapeLayer.strokeColor = strokeColor.CGColor 
     } 
    } 

    /// Center of main circle is in center top 

    private var pullDownCenter: CGPoint { 
     return CGPoint(x: bounds.size.width/2.0, y: bounds.size.width/2.0) 
    } 

    /// Radius of view spans width of view 

    private var radius: CGFloat { 
     return bounds.size.width/2.0 
    } 

    override var frame: CGRect { 
     get { 
      return super.frame 
     } 
     set { 
      super.frame = newValue 
      updatePath() 
     } 
    } 

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

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

    /// Update the path, add shape layer, and add gesture recognizer 

    private func configureView() { 
     shapeLayer.fillColor = fillColor.CGColor 
     shapeLayer.strokeColor = strokeColor.CGColor 
     shapeLayer.lineWidth = lineWidth 

     updatePath() 
     layer.addSublayer(shapeLayer) 

     let pan = UIPanGestureRecognizer(target: self, action: #selector(RefreshView.handlePan(_:))) 
     addGestureRecognizer(pan) 
    } 

    /// Update path 

    private func updatePath() { 
     shapeLayer.path = stretchyCirclePathWithCenter(pullDownCenter, radius: radius, yOffset: yOffset).CGPath 
    } 

    override func prepareForInterfaceBuilder() { 
     super.prepareForInterfaceBuilder() 
     yOffset = yOffsetMax 
    } 

    // MARK: Gesture Recognizer 

    private var yOffset: CGFloat = 0.0 { didSet { updatePath() } } 
    private var yOffsetMax: CGFloat { return bounds.size.width * 1.5 } 
    private var yOldOffset: CGFloat = 0.0 

    func handlePan(gesture: UIPanGestureRecognizer) { 
     if gesture.state == .Began { 
      yOldOffset = yOffset 
     } else if gesture.state == .Changed { 
      yOffset = yOldOffset + max(0, min(gesture.translationInView(gesture.view).y, yOffsetMax)) 
     } else if gesture.state == .Ended || gesture.state == .Cancelled { 
      animateBackToCircle() 
     } 
    } 

    // MARK: Animation 

    private var displayLink: CADisplayLink? 
    private var duration: CGFloat? 
    private var startTime: CFAbsoluteTime? 
    private var originalOffset: CGFloat? 

    private func animateBackToCircle() { 
     displayLink = CADisplayLink(target: self, selector: #selector(RefreshView.handleDisplayLink(_:))) 
     duration = 0.5 
     originalOffset = yOffset 
     startTime = CFAbsoluteTimeGetCurrent() 
     displayLink?.addToRunLoop(NSRunLoop.mainRunLoop(), forMode: NSRunLoopCommonModes) 
    } 

    func handleDisplayLink(displayLink: CADisplayLink) { 
     let percent = CGFloat(CFAbsoluteTimeGetCurrent() - startTime!)/duration! 

     if percent < 1.0 { 
      yOffset = originalOffset! * (1.0 - sin(percent * CGFloat(M_PI_2))) 
     } else { 
      self.displayLink?.invalidate() 
      self.displayLink = nil 
      updatePath() 
     } 
    } 

    // MARK: Stretch circle path 

    private func stretchyCirclePathWithCenter(center: CGPoint, radius: CGFloat, yOffset: CGFloat = 0.0) -> UIBezierPath { 
     func pointWithCenter(center: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint { 
      return CGPoint(x: center.x + radius * cos(angle), y: center.y + radius * sin(angle)) 
     } 

     if yOffset == 0 { 
      return UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: 2.0 * CGFloat(M_PI), clockwise: true) 
     } 

     let lowerRadius = radius * (1 - yOffset/yOffsetMax * 0.5) 
     let yOffsetTop = yOffset/4 
     let yOffsetBottom = yOffset/1.5 
     let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: CGFloat(M_PI), endAngle: 0, clockwise: true) 
     path.addCurveToPoint(CGPoint(x: center.x + lowerRadius, y:center.y + yOffset), controlPoint1: CGPoint(x: center.x + radius, y:center.y + yOffsetTop), controlPoint2: CGPoint(x: center.x + lowerRadius, y:center.y + yOffset - yOffsetBottom)) 
     path.addArcWithCenter(CGPoint(x: center.x, y:center.y + yOffset), radius: lowerRadius, startAngle: 0, endAngle: CGFloat(M_PI), clockwise: true) 
     path.addCurveToPoint(CGPoint(x: center.x - radius, y:center.y), controlPoint1: CGPoint(x: center.x - lowerRadius, y:center.y + yOffset - yOffsetBottom), controlPoint2: CGPoint(x: center.x - radius, y:center.y + yOffsetTop)) 

     return path 
    } 

} 

Это делает что-то вроде:

enter image description here

Очевидно, не стесняйтесь играть по пути, как вы сочтете нужным, добавьте дополнительные расцветки, такие как прядильная стрелка или что-то еще и т. д. Но это иллюстрирует основы построения пути безье, растягивает его жестом и оживляет его до круговой формы.

+0

Как сделать эту анимацию по контурным линиям? И с обеих сторон справа и слева –

+0

Я не понимаю, что вы подразумеваете под «контурными линиями». Я бы на самом деле предложил вам просто написать свой собственный вопрос ... – Rob

Смежные вопросы