2016-05-17 50 views
0

我有一个UIView的有边界的圈子中,像这样如何使拉伸在iOS

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() 
    } 
} 

这将画一个圆,但改变taperFactorheightFactor会给一个拉伸效果。将heightFactor设置为1.0将意味着图形将占据视图的整个高度,并将taperFactor设置为1.0将导致图形的尾部与顶部圆形一样宽。

要获得如何改变heightFactortaperFactor改变图纸的感觉,你可以再添加一个平移手势识别这一观点,并在它的行动这样说:

@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 
    } 

您可以更改heightFactortaperFactor达到不同的效果。这只是如何做一个刷新控制器的一部分,但似乎是你的问题的主旨。

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