IOS implements pulse radar and dynamic additions and deletions by swift-thanks for sharing

Source: Internet
Author: User
Tags border color vars radar

Swift after Xcode6 Beta4 a version of the update, basically can be used as a production tool, although some places and objc than to "lag" some, but also harmless. This is developed with Xcode6 Beta4+ios SDK 8.0, and if you use OBJC, just replace some syntax with the calling method. Final effect: This effect is converted from MOV file to GIF, and CSDN does not support image upload larger than 2M, Youku AddressCreate a basic animation

This effect is converted from MOV file to GIF, and CSDN does not support image upload greater than 2M, Youku address

Create a single View application project, and then create a swift file that I created called "Pulsingradarview", which is currently structured as:

The Viewcontroller holds a optional Pulsingradarview attribute, which indicates that it can be nil and then do a simple initialization work in Viewdidload:

Class Viewcontroller:uiviewcontroller {

var radarview:pulsingradarview!

Override Func Viewdidload () {

Super.viewdidload ()

Let radarsize = Cgsizemake (Self.view.bounds.size.width, Self.view.bounds.size.width)

Radarview = Pulsingradarview (Frame:cgrectmake (0, (self.view.bounds.size.height-radarsize.height)/2,

Radarsize.width,radarsize.height))

Self.view.addSubview (Radarview)

}

Override Func didreceivememorywarning () {

Super.didreceivememorywarning ()

Dispose of any resources the can be recreated.

}

}

The radar is round, so the width and height are self.view.bounds.size.width.

Pulsingradarview inside should be empty now, we first import Quartzcore, because later animation part will use Calayer, then rewrite DrawRect method:

Override func DrawRect (rect:cgrect) {

Uicolor.whitecolor (). Setfill ()

Uirectfill (Rect)

Let Pulsingcount = 6

Let animationduration:double = 4

var animationlayer = Calayer ()

for var i = 0; i < Pulsingcount; i++ {

var pulsinglayer = Calayer ()

Pulsinglayer.frame = CGRectMake (0, 0, rect.size.width, rect.size.height)

Pulsinglayer.bordercolor = Uicolor.graycolor (). Cgcolor

Pulsinglayer.borderwidth = 1

Pulsinglayer.cornerradius = RECT.SIZE.HEIGHT/2

var defaultcurve = camediatimingfunction (Name:kcamediatimingfunctiondefault)

var animationgroup = Caanimationgroup ()

Animationgroup.fillmode = Kcafillmodebackwards

Animationgroup.begintime = Cacurrentmediatime () + Double (i) * animationduration/double (Pulsingcount)

Animationgroup.duration = Animationduration

Animationgroup.repeatcount = HUGE

Animationgroup.timingfunction = Defaultcurve

var scaleanimation = cabasicanimation (keypath: "Transform.scale")

Scaleanimation.autoreverses = False

Scaleanimation.fromvalue = Double (0)

Scaleanimation.tovalue = Double (1.5)

var opacityanimation = cakeyframeanimation (keypath: "Opacity")

Opacityanimation.values = [Double (1), double (0.7), double (0)]

Opacityanimation.keytimes = [Double (0), double (0.5), double (1)]

Animationgroup.animations = [Scaleanimation,opacityanimation]

Pulsinglayer.addanimation (Animationgroup, Forkey: "Pulsing")

Animationlayer.addsublayer (Pulsinglayer)

}

Self.layer.addSublayer (Animationlayer)

}

Set the background color of the canvas to be white, Pulsingcount represents the number of waveforms, animationduration indicates the length of the animation, Then I created a animationlayer to hold all the animated layer------Pulsinglayer, so the layer structure looks like this:

Each pulsinglayer represents a circle in which the Pulsinglayer is initially initialized with a frame, border color, border size, and radius (radius), and radius is naturally half the width or height of itself.

Camediatimingfunction will talk later.

The next step is to create a animationgroup, because there are two animations that we need to use: scale (zoom), opacity (transparent), and the time it takes to control the start of the animation.

We use controlling Animation timing to illustrate the two properties of Fillmode and BeginTime in this article:

Each of the following squares represents 1 seconds, the following diagram represents 4 seconds, the animation time is 1.5 seconds, the yellow animation begins, the blue animation ends, the yellow to blue is the process of animation. As you can see, the blue part is white at the end, which means the entire animation ends and is removed from the layer.

The following picture starts the animation time offset of 1 seconds, the rest is unchanged.

By default, all layers are different regardless of the order in which they were created, their timelines are consistent, BeginTime is 0, the animation is started immediately after the layer is joined (or the animation is played at the current time), and if you want to offset 1 seconds (for example), To Cacurrentmediatime () +1, get the absolute time (in seconds) of the current system and +1. We want to achieve the pulse effect, it is necessary to make each animationgroup animation with a different begintime, so to set begintime = Cacurrentmediatime () + Double (i) * Animationduration/double (Pulsingcount), Swift does not support implicit type conversions, with a Double () explicit strong-turn.

But as you can see, the offset after the animation begins with an empty file, which is determined by Fillmode:

    • Kcafillmoderemoved default, the animation has no effect on the layer before the animation starts and after the animation, and what is the layer's original
    • Kcafillmodeforwards when the animation is finished, the layer will remain the last state of the animation
    • Kcafillmodebackwards and Kcafillmodeforwards, specific reference to the above
    • The effect of Kcafillmodeboth kcafillmodeforwards and Kcafillmodebackwards together
In our present case, Pulsinglayer is set over frame and border, so in the neutral period of the animation, Pulsinglayer will directly show a rounded circle (the animation has not yet started), of course, after the animation has been played once, This border is not displayed because the normal animation playback loop is entered and no gap period occurs. All we need to do is avoid the gap before the animation is played, that is, set fillmode = Kcafillmodebackwards (advance into the animation state). RepeatCount = huge is the literal meaning of the animation infinite loop (huge can be considered infinite, if it is OBJC, with Huge_val). The camediatimingfunction has several values preset by the system:
    • Kcamediatimingfunctionlinear linear, i.e. constant speed
    • Kcamediatimingfunctioneasein, slow down, fast.
    • Kcamediatimingfunctioneaseout first, then slow.
    • Kcamediatimingfunctioneaseineaseout Slow and slow
    • Kcamediatimingfunctiondefault the actual effect is faster when the animation starts and when the animation is played, it will slow down at the end
Camediatimingfunction support is customized. We set the timingfunction to Kcamediatimingfunctiondefault, which can make the animation play more dynamic. The next scale animation is very simple, from 0 (0 times times) to 1.5 (zoom 1.5 times times) transformation. Opacity transparent animation only set values and their corresponding keytimes on the line, it is important to note that Keytimes represents the time scale, the value of 0 to 1, such as values of the first element is 1,keytimes the first element is 0, indicating that the animation begins, The second element of opacity for 1;values is 0.7,keytimes the second element is 0.5, which means that when the animation is half-played, opacity is 0.7, and so on, it is freely customizable. It then encapsulates the individual scale animation and the opacity animation into the Animationgroup, adding Pulsinglayer to Pulsinglayer,animationlayer, which contains two animated Animationgroup, Finally, add this animationlayer that contains all the animated layers. Dynamic increment and decrement elements

This effect is converted from MOV file to GIF, and CSDN does not support image upload greater than 2M, Youku address

The animation section has been completed, and next we'll add an interface to Pulsingradarview that supports adding and subtracting elements. First, add two attributes to Pulsingradarview:

Class Pulsingradarview:uiview {

Let ItemSize = Cgsizemake (44, 44)

var items = Nsmutablearray ()

The first one is the size of each item, and the second is used to store all of the item. Add Addorreplaceitem public interface:

Func Addorreplaceitem () {

Let MaxCount = 10

var Radarbutton = UIButton (frame:cgrectmake (0, 0, itemsize.width, itemsize.height))

Radarbutton.setimage (UIImage (named: "UK"), ForState:UIControlState.Normal)

var center = Generatecenterpointinradar ()

Radarbutton.center = Cgpointmake (center.x, CENTER.Y)

Self.addsubview (Radarbutton)

Items.addobject (Radarbutton)

If Items.Count > MaxCount {

var view = Items.objectatindex (0) as UIView

View.removefromsuperview ()

Items.removeobject (view)

}

}

Maxcount is the maximum value of the item in the circle, which simply writes dead, and you can open it up to become a public property. Each item here is UIButton, set a picture after initialization, and the Generatecenterpointinradar method returns the center coordinate of a circle, which is generated only within the diameter of the circle and released later. Finally determine if there is more than maxcount, if exceeded, the first added item removed. Before releasing the Generatecenterpointinradar method, we first need to understand which range is our coordinate generation range: As you know, the basic shape of the view is the rectangle (the red area), and the DrawRect is based on Rect, But our radar is circular, that is, the blue area is our target range, so the coordinates generated around the center of the Green Dot (circle), let us reopen the math textbook, see High School mathematics to the definition of trigonometric functions:
In
a planar Cartesian coordinate system, the origin is the center, and 1 is the radius to draw a circle, the circle X axis at point A. With O as the center of rotation, a point counter-clockwise rotation of a certain angle α to point B, set at this time the coordinates of the B point is (x, y), then the value of y is called the sine of α, recorded as Sine; At this point the value of x is called the cosine of α, recorded as Cosα;y and x ratio y/x is called α Tanα.
There is also a very important formula: the parametric equation of a circle: the parametric equation of a circle with a radius of R is x=a+r*cosθ, y=b+r*sinθ, (where θ is the parameter), with the Point O (A, b) as the center .So far, the idea is clear, and here's how the Generatecenterpointinradar approach is implemented:

Private func Generatecenterpointinradar ()-cgpoint{

var angle = Double (arc4random ())% 360

var radius = double (arc4random ())% (double) ((Self.bounds.size.width-itemsize.width)/2)

var x = cos (angle) * radius

var y = sin (angle) * radius

Return Cgpointmake (CGFloat (x) + SELF.BOUNDS.SIZE.WIDTH/2, CGFloat (y) + SELF.BOUNDS.SIZE.HEIGHT/2)

}

We first randomly generate an angle within the 360° ( θ), and then randomly generate a value within the radius, as a new radius r, using the formula we get the X, y points, a center (a, A, a, b) is auxiliary, you can generate a coordinate, this coordinate is returned is based on the center of the point, So in the Addorreplaceitem this interface we get the coordinates can be directly as center to use, which is actually a completely adopted formula algorithm. In this way, Addorreplaceitem this interface is also completed, we put Viewcontroller in the call also perfect, specific, in the Viewdidload method of the last add a timer, This timer calls Addorreplaceitem every 0.5 seconds: [OBJC]View PlainCopyprint?
    1. Nstimer. Scheduledtimerwithtimeinterval (0. 5, Target:radarview, selector:selector (" Addorreplaceitem "), UserInfo: Nil, repeats: true)
The timer must call the Invalidate () method when not in use, and before the Viewcontroller is destroyed, the Viewcontroller will not be released, and it will never be refactored. Here we do not consider so much, after all, there is only one page, and in the real scene will not be so used, more often in the network request callback to deal with. In this way, the dynamic additions and deletions have been completed, but is it perfect? Apparently not.

Optimization optimization is more about fixing bugs than optimization. Obviously, in the previous step, the dynamically generated elements overlap, which is not acceptable, and we can prevent this from happening with just a little bit of change. We are now building each item's center, there is no comparison with the existing item, this is a more cost-performance operation, if your itemsize too large, maxcount too much, which can even lead to a dead loop, if that is the case, You may be restricting itemsize and maxcount, as well as controlling the number of loops, and if you make too many loops when you generate an item's center, you can be considered as a dead loop, in which case you can only recalculate the existing center s。 This is not considered in this extreme situation, because the current itemsize and maxcount coordination, there will be no cycle of death. We add a Itemframeintersectsinotheritem private method to determine if there is overlap with the previously generated center:

Private func Itemframeintersectsinotheritem (frame:cgrect), Bool {

For item in Items {

If Cgrectintersectsrect (Item.frame, frame) {

return True

}

}

return False

}

Receives a frame, and then compares each item, if the overlap returns true, and vice versa returns false. Modifications in the Addorreplaceitem method:

......

do {

var center = Generatecenterpointinradar ()

Radarbutton.center = Cgpointmake (center.x, CENTER.Y)

} while (Itemframeintersectsinotheritem (Radarbutton.frame))

......

Wrap up the center with a do-while loop. As a result, the resulting elements will not overlap.

Optimization II I'm going to add a little animation to the display and removal of each item to avoid being too blunt and to implement it in a derived class:

Class Prbutton:uibutton {

Init (frame:cgrect) {

Super.init (Frame:frame)

Self.alpha = 0

}

Override Func Didmovetowindow () {

Super.didmovetowindow ()

If Self.window {

Uiview.animatewithduration (1, animations: {

Self.alpha = 1

})

}

}

}

Replace the UIButton in Addorreplaceitem with Prbutton so that when the item is added there is a simple transition animation, and when I'm ready to rewrite Removefromsuperview, I get a little bit of a problem:
In Swift, the closure is not super, that's all it can do:

Override Func Removefromsuperview () {

Uiview.beginanimations ("", Context:nil)

Uiview.setanimationduration (1)

Self.alpha = 0

Uiview.setanimationdidstopselector (Selector ("Callsuperremovefromsuperview"))

Uiview.commitanimations ()

}

Private Func Callsuperremovefromsuperview () {

Super.removefromsuperview ()

}

You should be able to see the full effect when you run it. Optimization Three is also a bug fix. If you press the home button while the animation is playing (the simulator presses COMMAND+SHIFT+H), the following occurs: This is because all animations are removed when the home button is pressed, specifically, Each layer invokes the Removeallanimations method. If we want to continue the animation when we go back to the application, we need to listen to the system uiapplicationdidbecomeactivenotificationNotice:

Weak var animationlayer:calayer?

Override Init (Frame:cgrect) {

Super.init (Frame:frame)

Nsnotificationcenter.defaultcenter (). Addobserver (Self,

Selector:selector ("Resume"),

Name:uiapplicationdidbecomeactivenotification,

Object:nil)

}

Required Init (coder Adecoder:nscoder) {

FatalError ("Init (coder:) have not been implemented")

}

Func Resume () {

If let Animationlayer = self.animationlayer {

Animationlayer.removefromsuperlayer ()

Self.setneedsdisplay ()

}

}

Deinit {

Nsnotificationcenter.defaultcenter (). Removeobserver (self)

}

In this way, the animation can be returned to the application of the time to start again, I put the Animationlaye in the form of weak reference to the property inside, this is to be in the resume of good convenience to judge. GitHub If this article has any questions, please point out in time, in order to avoid unnecessary trouble for the later, greatly appreciated! The version on Updated:github has been updated to Xcode6 Beta6, before downloading and then updating Xcode's friends if the compilation appears __tfss26_forcebridgefromobjectivecu__ Ftpss9anyobject_mq__q_ and so on, please see here. Updated to Xcode on------------------------------------------------------------------------------------------------------GitHub 6.

IOS implements pulse radar and dynamic additions and deletions by swift-thanks for sharing

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.