2016-05-17 91 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