2016-05-17 9 views
0

ich eine UIView habe mit einem Kreis begrenzt, so etwas wie dieseWie man einen Kreis Effekt auf ios macht Stretching

enter image description here

Ich mag einen Effekt machen wie Stretching es, siehe folgendes Bild

enter image description here

Es mag eine pullToRefresh Wirkung gibt es pullToRefresh viele Bibliotheken oder opensources zu machen, aber sie sind immer zu Tableview gehört. Ich möchte nur den Stretching-Effekt unabhängig machen. Ich habe nur ein eingekreist UIView und es gestreckt ist, wenn ich es ziehen

Wie kann ich es machen

Antwort

0

Es gibt bereits eine Kontrolle für Sie: circle with strech

Abgesehen von oben ich werde einen Versuch geben, dieser Ansatz auch: Ich denke, dass Sie voll gestrecktes Bild haben können, durch weiße Ansicht darüber versteckt. Wenn der Benutzer nach unten oder oben wippt, können Sie einen Teil des gestreckten Bildes anzeigen/ausblenden.

Wenn der Benutzer nicht gewischt hat, dann zeigen Sie das nicht gestreckte Kreisbild an, und wenn der Benutzer einen Threshold-Abstand gewischt hat, zeigen Sie das vollständig gestreckte Bild an.

0

Sie müssen wahrscheinlich benutzerdefinierte Zeichnung erstellen. Wenn Sie nur eine Ansicht strecken, wird das Aliasing verursacht und die Ansicht wird verformt.

Um dies zu tun, sollten Sie die Unterklasse UIView und überschreiben drawRect, um eine Art von Zeichnung zu tun, die auf Benutzerinteraktion reagieren kann.

Ich habe eine Beispielklasse erstellt:

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

Dies wird einen Kreis zeichnen, sondern taperFactor und heightFactor Wechsel wird eine Dehnungseffekt geben. Wenn Sie heightFactor auf 1.0 setzen, bedeutet dies, dass die Zeichnung die gesamte Höhe der Ansicht einnimmt und die Einstellung taperFactor auf 1,0 bewirkt, dass das Ende der Zeichnung so breit wie der obere Kreis ist.

Um ein Gefühl dafür zu bekommen, wie heightFactor und taperFactor ändert die Zeichnung ändern, können Sie dann eine Pfanne Gestenerkenner dieser Ansicht hinzufügen und darin Aktion ist so etwas sagen wie:

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

Sie heightFactor und taperFactor ändern um verschiedene Effekte zu erzielen. Dies ist nur ein Teil davon, wie man einen Refresh-Controller erstellt, scheint aber die Stoßrichtung Ihrer Frage zu sein.

3

Die Grundidee besteht darin, Bezier-Pfade zu verwenden, um die genaue Form anzugeben, nach der Sie suchen.Sie können dann Geste verwenden, um diese Form zu ändern und Display-Link zu verwenden, um die Rückkehr des gestreckten Kreis zurück in seine runde Form zu animieren:

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

} 

Dies macht wie etwas:

Klar

enter image description here

, Fühlen Sie sich frei, mit dem Pfad herumzuspielen, wie Sie es für richtig halten, fügen Sie zusätzliche Schnörkel hinzu, wie einen drehenden Pfeil oder was auch immer, etc. Aber dies veranschaulicht die Grundlagen des bézier Weges, den er mit Gesten dehnt und wieder in seine kreisförmige Form animiert.

+0

Wie mache ich diese Animation durch Konturlinien? Und auf beiden Seiten in der rechten und linken –

+0

verstehe ich nicht, was Sie "von Konturlinien" meinen. Ich würde vorschlagen, dass Sie nur Ihre eigene Frage stellen ... – Rob

Verwandte Themen