Currently in its own personal project, Swift is already being used to write code. This article shows you a activityindicator view that you have designed in your project.
Before we start, let's look at the final results, such as:
I recommend that you download this article for a complete project shared on GitHub so that you can follow this article to read the code.
Demand analysis
We need to implement a loading effect that customizes and Uiactivityindicatorview provides similar functionality. We'll use the Core Graphics to draw this effect and make it move.
Let us first analyze the composition of this control, for our actual coding to provide specific ideas.
First, this loading is a circle of 8 arcs.
We're going to draw arcs first:
Draw 8 arcs like this, and surround them with a circle:
Then change the color of each arc by repeating it to make it move.
We inherit UIView, rewrite the DrawRect method to draw the interface, the first step to get the context of the current drawing:
1 |
let context = UIGraphicsGetCurrentContext() |
Drawing arcs
Here we use the Uibezierpath class to build the path and then draw the arc by drawing the path.
1234567891011121314151617 |
// 初始化一个 UIBezierPath 实例
let arcPath = UIBezierPath()
// 构建Arc路径
arcPath.addArcWithCenter(CGPointMake(CGFloat(self.frame.size.width/2), CGFloat(self.frame.size.height/2)), radius: CGFloat(Config.CC_ARC_DRAW_RADIUS), startAngle: CGFloat(DegreesToRadians(startAngle)), endAngle: CGFloat(DegreesToRadians(startAngle + Config.CC_ARC_DRAW_DEGREE)), clockwise:
true
)
// 把路径添加到当前绘图的上下文
CGContextAddPath(context, arcPath.CGPath)
// 设置线段宽度
CGContextSetLineWidth(context, CGFloat(Config.CC_ARC_DRAW_WIDTH))
// 设置线段颜色
CGContextSetStrokeColorWithColor(context, strokeColor)
// 绘制
CGContextStrokePath(context)
|
By the way above, we can successfully draw an arc. which
1 |
func addArcWithCenter(center: CGPoint, radius: CGFloat, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool) |
The interpretation of this method's build path is that center is the dot coordinate, radius is radius, startangle is the starting Radian, Endangle is the arc of the end, clockwise is the clockwise or counterclockwise.
Draw 8 Arcs
When we can successfully draw an arc in the drawing context, we should start by drawing 8 arcs in the correct position and take a different color.
Here are some parameter settings, including information such as radius, width, color, etc.:
12345678 |
struct Config {
static let CC_ACTIVITY_INDICATOR_VIEW_WIDTH = 40
static let CC_ARC_DRAW_PADDING = 3.0
static let CC_ARC_DRAW_DEGREE = 39.0
static let CC_ARC_DRAW_WIDTH = 6.0
static let CC_ARC_DRAW_RADIUS = 10.0
    
Static let cc_arc_draw_colors = [uicolor (red: 242/255.0, green: 242/255.0, blue: 242/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 230/255.0, green: 230/255.0, blue: 230/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 179/255.0, green: 179/255.0, blue: 179/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 128/255.0, green: 128/255.0, blue: 128/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 128/255.0, green: 128/255.0, blue: 128/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 128/255.0, green: 128/255.0, blue: 128/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 128/255.0, green: 128/255.0, blue: 128/255.0, alpha: 1.0). Cgcolor, uicolor (Red: 128/255.0, green: 128/255.0, blue: 128/255.0, alpha: 1.0). CGcolor]
}
|
We can draw 8 arcs in the DrawRect method, and in this case the complete code looks like this:
12345678910111213141516171819 |
override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
var
startAngle = Config.CC_ARC_DRAW_PADDING
for
index
in
1...8 {
let arcPath = UIBezierPath()
arcPath.addArcWithCenter(CGPointMake(CGFloat(self.frame.size.width/2), CGFloat(self.frame.size.height/2)), radius: CGFloat(Config.CC_ARC_DRAW_RADIUS), startAngle: CGFloat(DegreesToRadians(startAngle)), endAngle: CGFloat(DegreesToRadians(startAngle + Config.CC_ARC_DRAW_DEGREE)), clockwise:
true
)
CGContextAddPath(context, arcPath.CGPath)
startAngle += Config.CC_ARC_DRAW_DEGREE + (Config.CC_ARC_DRAW_PADDING * 2)
CGContextSetLineWidth(context, CGFloat(Config.CC_ARC_DRAW_WIDTH))
let colorIndex = abs(index - self.animateIndex)
let strokeColor = Config.CC_ARC_DRAW_COLORS[colorIndex]
CGContextSetStrokeColorWithColor(context, strokeColor)
CGContextStrokePath(context)
}
}
|
Draws 8 times with a For loop, produces 8 arcs, and sets a different color. Here the Self.animateindex is used to track the position of the lightest arc of the first color of the entire animation. Gets the color that the current arc should display by using it and the absolute value of the current index.
Move it
When designing a activityindicator view, we should, like the Uiactivityindicatorview provided by Uikit, implement at least these three sets of APIs:
123 |
func startAnimating() func stopAnimating() func isAnimating() -> Bool |
Here we use a timer to change the value of the self.animateindex and constantly redraw the current view to produce an animated effect, and the code looks like this:
12345678910111213141516171819202122232425262728293031 |
// 使用该值驱动改变圆弧颜色,产生动画效果
private
var
animateIndex: Int = 1
// 动画的Timer
private
var animatedTimer: NSTimer?
// timer响应的事件,在这里setNeedsDisplay让UIKit重画当前视图,然后不断改变animateIndex值。
@objc private func animate () {
if
!self.hidden {
self.setNeedsDisplay()
self.animateIndex++
if self.animateIndex > 8 {
self.animateIndex = 1
}
}
}
// 开始动画
func startAnimating () {
if
self.hidden {
self.hidden =
false
}
if
let timer = self.animatedTimer {
timer.fire()
}
else
{
self.animatedTimer = NSTimer(timeInterval: 0.1, target: self, selector:
"animate"
, userInfo: nil, repeats:
true
)
NSRunLoop.currentRunLoop().addTimer(self.animatedTimer!, forMode: NSRunLoopCommonModes)
}
}
|
Used here
1 |
init(timeInterval ti: NSTimeInterval, target aTarget: AnyObject, selector aSelector: Selector, userInfo: AnyObject?, repeats yesOrNo: Bool) -> NSTimer |
Instead of using
1 |
class func scheduledTimerWithTimeInterval(ti: NSTimeInterval, target aTarget: AnyObject, selector aSelector: Selector, userInfo: AnyObject?, repeats yesOrNo: Bool) -> NSTimer |
The reason to build the timer is that when we use our own activityindicator view, we may put it on the Uiscrollview. This time the timer created with Scheduledtimerwithtimeinterval is added to the current run loop, while Uiscrollview receives the user interaction event, the main thread run Loop is set to Uitrackingrunloopmode. This time will cause the timer to fail. In a more detailed answer, I'm walking into the world of Run Loop (a): What is a run loop? is stated in the article.
Summarize
By this time, we should be able to see and the same animation effect. However, when writing a custom control that is available for use, you should consider more detail work. For example, initialization, view removal, intrinsiccontentsize, support for @IBInspectable and @IBDesignable, etc., to make the developer of our control more friendly. More detailed code and demo can go here to view: Https://github.com/yechunjun/CCActivityIndicatorView
Build your custom Activityindicator View with Swift