[IOS animation]-calayer visual effects

Source: Internet
Author: User
Tags home screen

Visual effects

Well, circles and ellipses are good, but what about rectangles with rounded corners?

Can we do that now?

Steve Jobs

We discussed the frame of the layer in chapter three, "Layer geometry", and the second chapter, "Homestay Map", discusses the layer's homestay map. But a layer is not just a container for pictures or colors, but also a series of built-in features that make it possible to create beautiful, elegant, and impressive interface elements. In this chapter, we will explore some visual effects that can be achieved by using the Calayer property.

Rounded Corners

Rounded rectangles are an iconic aesthetic feature of iOS. This is reflected in every location on iOS, whether it's a home screen icon, a warning bullet box, or even a text box. Depending on the popularity, you might think there must be a way to easily create rounded corners without using Photoshop. Congratulations, you guessed it.

Calayer has a conrnerRadius property called properties that control the curvature of the layer's corners. It is a floating-point number that defaults to 0 (0 is right-angled), but you can set it to any value. By default, this curvature value affects only the background color without affecting the background picture or sublayer. However, if masksToBounds set to Yes, everything inside the layer will be intercepted.

We can demonstrate this effect through a simple project. In Interface Builder, we put some views, and they have some sub-views. And some of these sub-views are out of bounds (4.1). You may not be able to see them out of bounds, because when editing the interface, the excess is always cut off by interface builder. But you can trust me:)

Figure 4.1 Two large white views, they all contain smaller red views.

Then in the code, we set the radius of the corner to 20 points, and cut out the excess portion of the first view (see Listing 4.1). Technically, these properties can be set directly on the interface builder's probe board by "User defined runtime Properties" and by ticking the clip subviews selection box. However, in this example, the code can be expressed more clearly. Figure 4.2 is the result of running the code

Listing 4.1 sets cornerRadius andmasksToBounds

@interface Viewcontroller () @property (nonatomic, weak) Iboutlet UIView *layerview1; @property (nonatomic, weak) Iboutlet UIView *layerview2; @end @implementation viewcontroller-(void) viewdidload{???  [Super Viewdidload];  Set the corner radius on our layers  Self.layerView1.layer.cornerRadius = 20.0f;  Self.layerView2.layer.cornerRadius = 20.0f;  Enable clipping on the second layer  self.layerView2.layer.masksToBounds = YES;} @end

In the image on the right, the Red Child view is cropped along the corner radius

As you can see, the child view on the right is clipped along the boundary.

It is also not impossible to control the fillet curvature of each layer individually. If you want to create a layer or view with some rounded corners at a right angle, you might need a different approach. For example, use a layer mask (described later in this chapter) or Cashapelayer (see Chapter Sixth, "dedicated layers").

Layer border

Calayer the other two very useful properties are borderWidth the and borderColor . Together, they define the drawing style for the edges of the layer. This line (also called a stroke) is drawn along the layer and bounds also contains the corners of the layer.

borderWidthis a floating-point number that defines the border thickness in points, which defaults to 0. borderColor Defines the color of the border, which is black by default.

borderColoris a cgcolorref type, not a uicolor, so it is not a cocoa built-in object. However, you must also know the layer reference borderColor , although the attribute declaration does not prove it. The CGColorRef behavior in quoting/releasing is NSObject very similar. However, OBJECTIVE-C syntax does not support this practice, so CGColorRef even strong references can only be declared by the Assign keyword.

The border is drawn within the bounds of the layer, and before all child content, also before the child layer. If we add a border to the layer in the previous example (listing 4.2), you can see what's going On (4.3).

Listing 4.2 plus borders

@implementation viewcontroller-(void) viewdidload{  [Super Viewdidload];  Set the corner radius on our layers  Self.layerView1.layer.cornerRadius = 20.0f;  Self.layerView2.layer.cornerRadius = 20.0f;  Add a border to our layers  self.layerView1.layer.borderWidth = 5.0f;  Self.layerView2.layer.borderWidth = 5.0f;  Enable clipping on the second layer  self.layerView2.layer.masksToBounds = YES;} @end

  

Figure 4.3 Adding a border to a layer

Careful observation will find that the border does not calculate the shape of the homestay or sublayer, if the layer's sublayers are beyond the bounds, or if the boarding chart has a transparent mask in the transparent area, the border will still be drawn along the boundary of the layer (4.4).

Figure 4.4 The border is a change from the boundary of the layer, not the contents of the layer

Shadow

Another common feature of iOS is the shadow. Shadows can often reach the effect of layer depth hints. It can also be used to emphasize the layers and priorities being displayed (such as a pop-up box before other views), but sometimes they are purely decorative purposes.

Give the shadowOpacity property a value that is greater than the default value (that is, 0), and the shadow can be displayed below any layer of intent. shadowOpacityis a floating-point number that must be between 0.0 (invisible) and 1.0 (completely opaque). If set to 1.0, a slightly blurred black shadow is displayed slightly above the layer. To change the performance of the shadow, you can use Calayer's other three properties: shadowColor , shadowOffset and shadowRadius .

Obviously, the shadowColor property controls the color of the shadow, as well as borderColor backgroundColor its type CGColorRef . Shadows are black by default, and most of the time you need shadows that are also black (other shades of color look not to be a little bit strange ...). )。

shadowOffsetproperty controls the direction and distance of the shadow. It is a CGSize value, width controls the lateral displacement of the shadow, and the height controls the longitudinal displacement. shadowOffsetThe default value is {0,-3}, meaning that the shadow has an upward displacement of 3 points relative to the y-axis.

Why do you want to default up the shadow? Although core animation evolved from a layer suite (which can be thought of as a private animation frame created for iOS), it was on Mac OS, as mentioned earlier, the y-axis is reversed. This causes the default 3-point displacement of the shadow to be upward. On the Mac, shadowOffset the default value is shadow down so you can understand why the shadow direction on iOS is up (4.5).

Figure 4.5 shows up on iOS (left) and Mac OS (right) shadowOffset .

Apple prefers that the shadow of the user interface should be vertically downward, so it is a practice to set the shadow width to 0 on iOS and then set the height to a positive value.

shadowRadiusproperty controls the Blur of the shadow, and when its value is 0, the shadow has a very definite boundary line like the view. When the value is getting bigger, the boundary line will look more and more blurred and natural. Apple's own application design is more in favor of natural shadows, so a non-0 value is more appropriate.

Generally speaking, if you want the view or control to be very visible and independent of the background (such as a popover mask layer), you should shadowRadius set a slightly larger value. The more blurred the shadow, the deeper the layer will look (4.6).

Figure 4.6 The larger shadow displacements and angular radii increase the depth of the layer, which is the sense of vision

Shadow cropping

Unlike the layer border, the layer's shadow inherits from the shape of the content, not the boundary and corner radius. To calculate the shape of the shadow, Core animation takes the homestay map (including the child view, if any) into account, and then uses these to perfectly match the layer shape to create a shadow (see Figure 4.7).

Figure 4.7 The shadow is determined based on the outline of the homestay map.

There is a headache when the shadow and clipping are related: The shadow is usually outside the layer's boundary, and if you turn on the masksToBounds attribute, all the content protruding from the layer will be cut off. If we add the shadow properties of the layer to our previous border example project, you will find the problem (see Figure 4.8).

Figure 4.8 maskToBounds property cuts out shadows and content

Technically, the result can be understandable, but it's not exactly the effect we want. If you want to trim along the content, you need to use two layers: an empty outer layer that only draws shadows, and a masksToBounds neatline layer with cropped content.

If we wrap the cropped view in a separate view on the right side of the previous project, we can solve the problem (4.9).

Figure 4.9, to the right, wrap the cropped view with an extra shadow transform view

We only use shadows on the outermost view, and the inner view is cropped. Listing 4.3 is the code implementation, and Figure 4.10 is the run result.

Listing 4.3 uses an additional view to solve the problem of shadow trimming

@interface Viewcontroller () @property (nonatomic, weak) Iboutlet UIView *layerview1; @property (nonatomic, weak) Iboutlet UIView *layerview2; @property (nonatomic, weak) Iboutlet UIView *shadowview; @end @implementation viewcontroller?-(void)  viewdidload{[Super Viewdidload];  Set the corner radius on our layers Self.layerView1.layer.cornerRadius = 20.0f;  Self.layerView2.layer.cornerRadius = 20.0f;  Add a border to our layers self.layerView1.layer.borderWidth = 5.0f;  Self.layerView2.layer.borderWidth = 5.0f;  Add a shadow to layerView1 self.layerView1.layer.shadowOpacity = 0.5f;  Self.layerView1.layer.shadowOffset = Cgsizemake (0.0f, 5.0f);  Self.layerView1.layer.shadowRadius = 5.0f;  Add same shadow to Shadowview (not layerView2) self.shadowView.layer.shadowOpacity = 0.5f;  Self.shadowView.layer.shadowOffset = Cgsizemake (0.0f, 5.0f);  Self.shadowView.layer.shadowRadius = 5.0f; Enable clipping on the second layer self.layerView2.layer.masksToBounds = YES;} @end

Figure 4.10 The right view, not the shaded view of the crop shadow.

shadowPathProperty

We already know that the layer shadow is not always square, but rather inherits from the shape of the layer content. This looks good, but the real-time calculation of Shadows is also a very resource-intensive one, especially when layers have multiple sublayers, and each layer has a transparent view of the homestay.

If you know in advance what your shadow shape will look like, you can improve performance by specifying one shadowPath . shadowPathis a CGPathRef type (a pointing CGPath pointer). CGPathis a core graphics object that is used to specify any vector graphic. This property allows us to specify the shape of the shadow separately from the layer shape.

Figure 4.11 shows the different shading settings for the same boarding chart. As you can see, the graph we use is simple, but its shadow may be any shape you want. Listing 4.4 is the code implementation.

Figure 4.11 Using the shadowPath specified arbitrary shadow shape

Listing 4.4 Creating a simple shadow shape

@interface Viewcontroller () @property (nonatomic, weak) Iboutlet UIView *layerview1; @property (nonatomic, weak) Iboutlet UIView *layerview2; @end @implementation viewcontroller-(void) viewdidload{  [Super Viewdidload];  Enable layer shadows  self.layerView1.layer.shadowOpacity = 0.5f;  self.layerView2.layer.shadowOpacity = 0.5f;  Create a square shadow  cgmutablepathref Squarepath = cgpathcreatemutable ();  Cgpathaddrect (Squarepath, NULL, self.layerView1.bounds);  Self.layerView1.layer.shadowPath = Squarepath; Cgpathrelease (Squarepath);  ? Create a circular shadow  cgmutablepathref Circlepath = cgpathcreatemutable ();  Cgpathaddellipseinrect (Circlepath, NULL, self.layerView2.bounds);  Self.layerView2.layer.shadowPath = Circlepath; Cgpathrelease (Circlepath);} @end

If it's a rectangle or a circle, it's CGPath pretty straightforward. But if it is a more complicated graph, the UIBezierPath class will be more appropriate, it is a objective-c wrapper class provided by Uikit on the Cgpath basis.

Layer Mask

Through masksToBounds the properties, we can crop the graph along the boundary, and through cornerRadius the properties we can also set a rounded corner. But sometimes what you want to show is not a rectangle or a rounded rectangle. For example, you want to show a picture with a star frame, or you want some scrolls to gradually fade into a background color instead of an abrupt boundary.

Using a 32-bit PNG image with an alpha channel is often the most convenient way to create a rectangle-free view, and you can assign it a transparent mask to implement. However, this method does not allow you to dynamically generate masks in a coded fashion, nor can you crop the sublayers or sub-views to the same shape.

Calayer has a property called mask to solve this problem. This property is itself a calayer type, with the same drawing and layout properties as other layers. It is similar to a sublayer, which is laid out relative to the parent layer (that is, the layer that owns the property), but it is not an ordinary sublayer. Unlike the sublayers that are drawn in the parent layer, the mask layer defines a portion of the visible area of the parent layer.

maskThe properties of a layer Color are irrelevant, and what really matters is the outline of the layer. maskproperties are like a cookie cutter, the mask solid portion of the layer is preserved and the others are discarded. (4.12)

If the mask layer is smaller than the parent layer, only the mask content within the layer is what it cares about, and everything else will be hidden.

Figure 4.12 Effect of the image and mask layer together

We'll show the code this process, create a simple project, and use the properties of the layer to work on the mask picture. For the sake of simplicity, we use Interface Builder to create a picture layer that contains uiimageview. So we just need the code to implement the mask layer. Listing 4.5 is the final code, and figure 4.13 is the result after the run.

Listing 4.5 applying a mask layer

@interface Viewcontroller () @property (nonatomic, weak) Iboutlet Uiimageview *imageview; @end @implementation viewcontroller-(void) viewdidload{  [Super Viewdidload];  Create mask layer  calayer *masklayer = [Calayer layer];  Masklayer.frame = self.layerView.bounds;  UIImage *maskimage = [UIImage imagenamed:@ "Cone.png"];  Masklayer.contents = (__bridge id) maskimage.cgimage;  Apply mask to image layer?  Self.imageView.layer.mask = Masklayer;} @end

Figure 4.13 After using mask the Uiimageview

The real calayer of the mask layer is that the mask diagram is not limited to static plots. Any layer that has layers can be used as mask attributes, which means that your mask can be generated in real time through code or even animations.

Tensile filtration

Finally let's talk about minificationFilter and magnificationFilter attributes. In general, when our view displays a picture, it should be displayed correctly (meaning that it is displayed on the screen in the correct proportions and 1:1 pixels in the correct way). The reasons are as follows:

    • can display the best picture quality, the pixels are neither compressed nor stretched.
    • It's better to use memory, because that's all you have to store.
    • The best performance of the CPU is not required for this additional calculation.

Sometimes, however, displaying a non-real-size picture is really the effect we need. For example, an avatar or thumbnail of a picture, such as a large image that can be dragged and stretched. In these cases, it is impractical to store different images for different sizes of the same picture.

When a picture needs to show a different size, an algorithm called stretch filtering works. It acts on the pixels of the original image and generates new pixels as needed to display on the screen.

In fact, redrawing the image size does not have a unified universal algorithm. This depends on the content that needs to be stretched, the need to zoom in or out, and so on. There CALayer are three types of tensile filtration methods available for this purpose:

    • Kcafilterlinear
    • Kcafilternearest
    • Kcafiltertrilinear

Minification (zoom Out) and magnification (enlarge image) The default filter is kCAFilterLinear that this filter uses a bilinear filtering algorithm, which in most cases is performing well. The bilinear filtering algorithm produces a smooth, well-behaved stretch by eventually generating new values for multiple pixel sampling. But when the magnification is larger, the picture is blurred.

kCAFilterTrilinearand kCAFilterLinear very similar, in most cases they do not see any difference. However, compared with the bilinear filtering algorithm, the three-wire filter algorithm stores the picture (also called multi-map) in the case of multiple sizes, and then obtains the final result by combining the storage of large and small graphs.

The advantage of this method is that the algorithm can get the desired result from a series of images that are already close to the final size, that is to say, do not sample many pixels synchronously. This not only improves performance, but also avoids the problem of sampling failure due to rounding errors in small probabilities

Figure 4.14 for larger graphs, bilinear filtering and tri-linear filtering perform better

kCAFilterNearestis a more arbitrary approach. From the name it is not difficult to see, this algorithm (also known as the recent filter) is to sample the nearest single pixel, regardless of other colors. This is very fast and does not blur the picture. However, the most obvious effect is to make the compressed picture worse, the image enlarged after the appearance of block or mosaic serious.

Figure 4.15 The recent filtering algorithm is much better for small graphs without slashes

In general, for smaller graphs or large graphs with very few diagonal lines, the most recent filtering algorithm preserves this distinct trait to show better results. But for most graphs, especially those with a lot of diagonal lines or curved contours, the recent filtering algorithm can result in worse results. In other words, linear filtering preserves the shape, and recent filtering preserves the difference in pixels.

Let's experiment. Let's change the clock item in chapter Three and display it in the LCD style digital way. We create a digital display using a simple pixel font (a font that is made of pixels, rather than a vector graphic), stored in a picture, and displayed (4.16) using the flattening technique introduced in chapter two.

Figure 4.16 A simple display of the LCD digital style pixel font using the flattening technique

We placed six views in Interface Builder, two in hours, minutes, seconds, and figure 4.17 shows how these six views are placed in Interface builder. If each has a fade-out outlets object will appear too much, so we use an IBOutletCollection object to connect them with the controller, so that we can access the view as an array. Listing 4.6 is the code implementation.

Listing 4.6 shows an LCD-style clock

@interface Viewcontroller () @property (nonatomic, Strong) Iboutletcollection (UIView) Nsarray *digitviews; @property ( Nonatomic, weak) Nstimer *timer;?? @end @implementation viewcontroller-(void) viewdidload{[Super Viewdidload];//get spritesheet image UIImage *digits = [U  IImage imagenamed:@ "Digits.png"]; Set up digit views for (UIView *view in self.digitviews) {//set contents view.layer.contents = (__bridge ID) Digi Ts.    Cgimage;    View.layer.contentsRect = CGRectMake (0, 0, 0.1, 1.0);  view.layer.contentsGravity = Kcagravityresizeaspect; }//start Timer self.timer = [Nstimer scheduledtimerwithtimeinterval:1.0 target:self selector: @selector (tick) UserInfo:  Nil Repeats:yes]; Set initial clock time [self tick];} -(void) Setdigit: (nsinteger) digit Forview: (UIView *) view{//adjust contentsrect to select correct digit view.layer.conte Ntsrect = cgrectmake (digit * 0.1, 0, 0.1, 1.0);} -(void) tick{//convert time to hours, minutes and seconds nscalendar *calendar = [[Nscalendar Alloc] Initwithcalendaridentifier:nsgregoriancalendar]; Nsuinteger units = Nshourcalendarunit | Nsminutecalendarunit |  Nssecondcalendarunit;  ?  nsdatecomponents *components = [Calendar components:units fromdate:[nsdate Date]];  Set hours [self SETDIGIT:COMPONENTS.HOUR/10 forview:self.digitviews[0]];  [Self setDigit:components.hour% forview:self.digitviews[1]];  Set minutes [self SETDIGIT:COMPONENTS.MINUTE/10 forview:self.digitviews[2]];  [Self SetDigit:components.minute% forview:self.digitviews[3]];  Set seconds [self SETDIGIT:COMPONENTS.SECOND/10 forview:self.digitviews[4]]; [Self setDigit:components.second% forview:self.digitviews[5];} @end

4.18, it really works, but the picture looks blurry. The default kCAFilterLinear options seem to disappoint us.

Figure 4.18 A blurred clock, caused by the default kCAFilterLinear

To be able to do this in figure 4.19, we need to add the following code to the FOR loop:

View.layer.magnificationFilter = Kcafilternearest;

Figure 4.19 Sets a clear display after the most recent filtering

Group Transparency

UIView has a alpha property called to determine the transparency of the view. Calayer has an equivalent property called opacity , both of which affect the sub-level. That is, if you set a property on a layer opacity , its sublayers will be affected by this.

The common practice of iOS is to set the alpha value of a space to 0.5 (50%) so that it appears to be rendered unusable. It's good for a standalone view, but it's a bit odd when a control has a child view, and figure 4.20 shows a custom UIButton with a uilabel embedded in it, an opaque button on the left and the same button for 50% transparency on the right. We can notice that the outline of the label inside is not the same as the background of the button.

Figure 4.20 The right-hand fade button, the label inside is clearly visible

This is caused by a mixture of transparency, and when you display a layer with 50% transparency, each pixel of the layer typically displays its own color, and the other half shows the color underneath the layer. This is the performance of normal transparency. But if the layer contains a sub-layer that also shows 50% transparent, the view you see, 50% comes from the child view, 25% comes in the color of the layer itself, and the other 25% comes from the background color.

In our example, the buttons and emoticons are all white backgrounds. Although they are all 50% visible, the combined visibility is 75%, so the area where the label is located does not look as transparent as the surrounding part. So it looks like the sub-view is highlighted, making the display look awful.

Ideally, when you set the transparency of a layer, you want it to contain the entire layer tree as a whole as a transparent effect. You can do this by setting the Yes in the Info.plist file UIViewGroupOpacity , but this setting affects the app and the app may be adversely affected. If UIViewGroupOpacity not set, IOS 6 and previous versions will default to No (there may be some changes in later versions).

Another way is that you can set up a calayer called shouldRasterize Property (see listing 4.7) for group transparency, and if it is set to Yes, the layer and its sublayers will be integrated into a whole picture before applying transparency, so there is no problem with transparency blending (4.21).

To enable the shouldRasterize properties, we set the properties of the layer rasterizationScale . By default, all layer stretches are 1.0, so if you use shouldRasterize properties, make sure you set the rasterizationScale properties to match the screen to prevent the retina screen from pixelated.

When shouldRasterize UIViewGroupOpacity together, performance issues arise (we will introduce the 12th "speed" and "Layer Performance" in chapter 15th), but the performance collisions are localized (the translator notes: This sentence needs to be translated).

Listing 4.7 using shouldRasterize attributes to resolve group transparency issues

@interface Viewcontroller () @property (nonatomic, weak) Iboutlet UIView *containerview; @end @implementation  viewcontroller-(UIButton *) custombutton{//create button CGRect frame = CGRectMake (0, 0, 150, 50);  UIButton *button = [[UIButton alloc] initwithframe:frame];  Button.backgroundcolor = [Uicolor Whitecolor];  Button.layer.cornerRadius = 10;  Add Label frame = CGRectMake (20, 10, 110, 30);  UILabel *label = [[UILabel alloc] initwithframe:frame];  Label.text = @ "Hello World";  Label.textalignment = Nstextalignmentcenter;  [Button Addsubview:label]; return button;}  -(void) viewdidload{[Super Viewdidload];  Create Opaque button UIButton *button1 = [self CustomButton];  Button1.center = Cgpointmake (50, 150);  [Self.containerview Addsubview:button1];  Create Translucent button UIButton *button2 = [self CustomButton];  ?  Button2.center = Cgpointmake (250, 150);  Button2.alpha = 0.5;  [Self.containerview Addsubview:button2];  Enable rasterization for the translucent buttonButton2.layer.shouldRasterize = YES; Button2.layer.rasterizationScale = [UIScreen mainscreen].scale;} @end

Figure 4.21 the revised figure

Summarize

This chapter describes some of the visual effects that you can apply to layers, such as rounded corners, shadows, and masks. We also learned about stretch filters and group transparency.

In the fifth chapter, "Transformations," we'll look at layer changes and 3D transformations.

[IOS animation]-calayer visual effects

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.