我有一个UIView的有边界的圈子中,像这样如何使拉伸在iOS
我想要像伸展它的效果的圆形效果,请参见下面的图像
它喜欢pullToRefresh效果,有许多库或opensources来做pullToRefresh,但它们总是属于tableview。我只是想独立做出伸展效果。我只是有一个盘旋的UIView和它的拉伸时,我把它
我怎样才能使它
我有一个UIView的有边界的圈子中,像这样如何使拉伸在iOS
我想要像伸展它的效果的圆形效果,请参见下面的图像
它喜欢pullToRefresh效果,有许多库或opensources来做pullToRefresh,但它们总是属于tableview。我只是想独立做出伸展效果。我只是有一个盘旋的UIView和它的拉伸时,我把它
我怎样才能使它
目前已经可以为你的控制: circle with strech
除了上面我会给一个尝试这种方法也是: 我认为你可以有完整的拉伸图像,隐藏在它上面的白色视图。当用户向下或向上滑动时,您可以显示/隐藏部分拉伸图像。
如果用户没有刷过,则显示未被刷新的圆形图像,并且如果用户已经刷新了预测距离,则显示完全拉伸的图像。
您可能需要自定义绘图。只是伸展一个视图会导致出现锯齿和视图变形。
要做到这一点,你应该继承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
达到不同的效果。这只是如何做一个刷新控制器的一部分,但似乎是你的问题的主旨。
基本的想法是使用贝塞尔路径来勾勒出您要查找的精确形状。然后,您可以使用手势来改变外形和使用显示器链接到动画拉伸圆的返回回其圆形形式:
@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
}
}
这使得类似:
显然,可以随意随意找到合适的路径,添加像旋转箭头或其他任何东西等额外的繁荣,但这说明了构建贝塞尔路径,用手势拉伸它,并将其恢复为圆形形状的基础知识。
如何通过轮廓线制作此动画?并在左右两边 –
我不明白你的意思是“等高线”。我实际上建议你只是发表你自己的问题...... – Rob