Reprinted from: http://www.cocoachina.com/ios/20131226/7614.html
This article can also be found here in English, Ray: This tutorial excerpt from the iOS 7 tutorial set, it is part of the iOS 7 feast, I hope you can like it. You may have noticed that there seems to be some paradox in IOS 7 that Apple is proposing to abandon the real world metaphor and materialization while encouraging the creation of a real user interface. What does this mean in practice? IOS 7 is designed to encourage the creation of a digital interface that responds to touch, gesture, and direction changes like real physical objects, rather than a simple stack of pixels. Finally, it is different from the form of the quasi-materialization, so that users and interface to create a deeper relationship. This task sounds daunting because it is much simpler to make a seemingly real digital interface than to do a real-life interface. Fortunately, you have some great new tools to help you accomplish this task: UIKit mechanics (Dynamics) and dynamic effects (motion effects). Translator Note: About UIKit Dynamics, I have discussed with many developers, with motivation, dynamic model, dynamic and other translation methods. But I think the translation is more appropriate for mechanics, I hope that the mechanical knowledge in the text will allow you to agree with my view.
1.UIKit Mechanics is a complete physics engine integrated into the UIKit. It allows you to create a real-world interface by adding behaviors such as gravity, adsorption (spring), and force (behavior). All you need to do is define the physical characteristics that your interface element needs to follow, leaving the mechanics of the engine to handle it.
2. Dynamic effects allow you to create a fairly cool parallax effect, such as the dynamic background of the IOS 7 home screen. Simply put, you can use the data provided by your phone's accelerometer to develop an interface that responds to changes in the direction of your phone. The use of both dynamic and mechanical effects is a powerful tool to bring the digital interface and experience to life. When your users see your app responding to their actions in a natural, dynamic way, they have a deeper connection to your application.
get started.UIKit mechanics is very interesting, the best way to learn is to start with a few small examples. Open Xcode, select File/new/project ... Then select Iosapplicationsingle View application and name the new project for Dynamicsplayground. When you are finished creating the project, open VIEWCONTROLLER.M and add the following code to the end of Viewdidload:
- uiview* square = [[UIView alloc] initWithFrame:
- CGRectMake (100, 100, 100, 100)];
- Square.backgroundcolor = [Uicolor Graycolor];
- [Self.view Addsubview:square];
The above code simply adds a block UIView to the interface. Compile and run, you can see the box as shown: If you run the APP on the real machine, try turning the phone upside down, or shaking it. Nothing ever happened? That's right, that should be the case. Because when you add a view to the interface, you want him to keep the frame stable until you add some mechanical behavior to the interface.
Add GravityContinue editing VIEWCONTROLLER.M and add the following instance variables:
- uidynamicanimator* _animator;
- uigravitybehavior* _gravity;
Add the following code at the end of the viewdidload:
- _animator = [[Uidynamicanimator alloc] initWithReferenceView:self.view];
- _gravity = [[Uigravitybehavior alloc] initwithitems:@[square];
- [_animator addbehavior:_gravity];
I'll explain the code later, and now you just need to compile and run your program. You should see the block gradually accelerating down until it falls off the screen, as shown in: In the code you just added, there are some mechanics-related classes:
1.UIDynamicAnimator is the UIKit physics engine。 This class records the various behaviors (such as gravity) that you add to the engine, and provides a global context. When you create an animated instance, you need to pass in a reference view to define the coordinate system.
2.UIGravityBehavior Abstract The behavior of gravity into a model,and exerts a force on one or more elements, allowing you to establish a physical interaction model. When you create a behavior instance, you need to associate it to a set of elements, typically a set of views. This allows you to choose the elements that are affected by the behavior, in this case the elements that are affected by gravity. Most behaviors have some configurable properties. For example, gravity behavior allows you to change its angle and magnitude. Try modifying these properties to make your object move upward, sideways, or obliquely at different accelerations. Note: About units: in physics, gravity (g) units are meters per square second, about 9.8 m/s2. According to Newton's second law, you can use the following formula to calculate the distance that the object moves under gravity:
distance = 0.5xgx time ^2In UIKit mechanics, the formula still applies, but the units are different. The meters per square second in the unit are replaced by pixels per square second. Based on the gravity parameter, apply Newton's second law you can still calculate the position of the view at any time. Do you really need to know this? Not necessarily, you just need to know that bigger g means dropping faster, but understanding the math behind it is good for the pros and cons.
Set BoundariesAlthough you can't see it, it still continues to fall when the block disappears at the edge of the screen. To keep it within the screen range, you need to define a boundary. Add another instance variable in VIEWCONTROLLER.M:
- uicollisionbehavior* _collision;
- Add the following code at the end of the viewdidload:
- _collision = [[Uicollisionbehavior alloc]
- Initwithitems:@[square]];
- _collision.translatesreferenceboundsintoboundary = YES;
- [_animator addbehavior:_collision];
The above code creates a collision behavior that defines one or more boundaries to determine how the related elements interact with each other. The above code does not explicitly add boundary coordinates, but instead sets the Translatesreferenceboundsintoboundary property to YES. This means that the bounds of the view provided to the uidynamicanimator is used as the boundary. Compile and run, and you'll see the block flick up and end up at the bottom of the screen, as shown in: This is a great behavior, especially if you see so little code volume.
Handling CollisionsNext you will add a fixed obstacle that will collide with the falling blocks and affect each other. Add the following code after adding the block code to the Viewdidload:
- uiview* barrier = [[UIView alloc] Initwithframe:cgrectmake (0, 300, 130, 20)];
- Barrier.backgroundcolor = [Uicolor Redcolor];
- [Self.view Addsubview:barrier];
To compile and run the app, you can see a red "obstacle" across the middle of the screen. But you will find that he does not play any role, the block goes straight through the barrier: This is obviously not what you want, it also shows the important point: mechanics affects only the view that is associated with the behavior. Here is a simple: Associate Uidynamicanimator to a reference view that provides a coordinate system, and then add one or more behaviors to exert force on the associated object. Most behaviors can be associated with multiple objects, and each object can be associated with multiple behaviors. Shows the behavior in the current app and their relationship. The behavior in the current code does not involve obstructions, so in the mechanics engine, the obstacle does not exist.
To make an object respond to a collisionIn order for the block to collide with the obstacle, locate the code that initializes the collision behavior and replace it with the following code:
- _collision = [[Uicollisionbehavior alloc] initwithitems:@[square, barrier]];
A collision instance needs to know every view it affects, so adding obstacles to the list makes the collision work for the obstacles as well. To compile and run the application, two objects collide and interact with each other, as shown in: The collision behavior creates a boundary around each of the associated objects, making these objects inaccessible from objects that can cross each other into entities. To update the previous one, the collision behavior is now associated with two views: but there are still some discrepancies. We want the obstacle to be non-movable, but under current settings, when two objects collide, the obstacle is knocked open and rotated to the bottom of the screen. What is even more strange is that the obstacles do not seem to be stationary after they bounce from the bottom. This is because gravity does not have an effect on the barrier, which also explains why it does not move before the block hits an obstacle. You need another way to solve the problem. Since the obstacle is not movable, then there is no need for the mechanics engine to know its existence. But how do we detect collisions?
Invisible borders and collisionsChange the initialization code of the collision behavior back to the original, so that he knows only the existence of the BLOCK:
- _collision = [[Uicollisionbehavior alloc] initwithitems:@[square];
Then, add the following boundary:
- Add a boundary that coincides with the top edge
- Cgpoint Rightedge = cgpointmake (barrier.frame.origin.x +
- Barrier.frame.size.width, BARRIER.FRAME.ORIGIN.Y);
- [_collision addboundarywithidentifier:@"barrier"
- FromPoint:barrier.frame.origin
- Topoint:rightedge];
The code above adds an invisible boundary, which is the upper boundary of the obstacle. The red barrier is still visible to the user, but the mechanics engine does not know its existence; instead, the added boundary is visible to the mechanics engine and invisible to the user. When the block falls, it appears to collide with the obstacle, but it encounters a non-moving boundary. Compile and run the app, and the effect you see is as follows: The block now bounces from the edge of the barrier, rotates, and then continues to fall to the bottom of the screen until it is still. So far, the strength of UIKit mechanics is evident: you can achieve quite complex results with just a few simple lines of code. There are a lot of complicated logic behind this, and in the next chapter we will deal with the concrete ways in which the mechanics engine interacts with the objects in the application.
behind the collisionEach mechanical action has an action property, and you can define a block to be executed at every step of the animation. Add the following code to Viewdidload:
- _collision.action = ^{
- NSLog (@"%@,%@",
- Nsstringfromcgaffinetransform (Square.transform),
- Nsstringfromcgpoint (Square.center));
- };
The above code records the core transform attribute of the midpoint position of the falling block. Compile and run the app, and you'll see debugging information in the Xcode console. In the first 400 milliseconds or so you will see something like this:
- 2013-07-26 08:21:58.698 dynamicsplayground[17719:a0b] [1, 0, 0, 1, 0, 0], {150, 236}
- 2013-07-26 08:21:58.715 dynamicsplayground[17719:a0b] [1, 0, 0, 1, 0, 0], {150, 243}
- 2013-07-26 08:21:58.732 dynamicsplayground[17719:a0b] [1, 0, 0, 1, 0, 0], {150, 250}
You can see that the mechanics engine constantly changes the midpoint of a block, or its frame, during animation. When the block hits an obstacle, it starts to spin, and the debugging information is similar to this:
- 2013-07-26 08:21:59.182 dynamicsplayground[17719:a0b] [0.10679234, 0.99428135,-0.99428135, 0.10679234, 0, 0], {198, 325 }
- 2013-07-26 08:21:59.198 dynamicsplayground[17719:a0b] [0.051373702, 0.99867952,-0.99867952, 0.051373702, 0, 0], {199, 331}
- 2013-07-26 08:21:59.215 dynamicsplayground[17719:a0b] [-0.0040036771, 0.99999201,-0.99999201,-0.0040036771, 0, 0], { 201, 338}
You can see that the mechanics engine locates the view based on the physical model and changes both the Transform property and the Frame property. Although the exact value of these attributes is not interesting, it is important that they change every moment of the day. So if you try to change the object's frame or transform property with code, the values will be overwritten. This means that when your object is under the control of the mechanics engine, you cannot scale your object by transform. The method name of the mechanical behavior is used items rather than views, because the object that wants to use the mechanical behavior only implements the Uidynamicitem protocol, which is defined as follows:
- @protocol Uidynamicitem
- @property (nonatomic, ReadWrite) Cgpoint Center;
- @property (nonatomic, readonly) cgrect bounds;
- @property (nonatomic, ReadWrite) Cgaffinetransform transform;
- @end
The Uidynamicitem protocol provides the mechanics engine with the ability to read and write center and transform properties, allowing it to move objects based on internal calculations. The bounds is also provided with Read permission to determine the size of the object, which is used not only when calculating the boundary of the object, but also in calculating the mass of the object when it is subjected to force. This protocol shows that the mechanics engine is not coupled to the uiview, but in fact there is a class in UIKit that implements the Protocol –uicollectionviewlayoutattributes. Therefore, the collection views can be animated by the mechanical engine.
Collision NotificationBy now, you have added some views and behaviors, and then let the mechanics engine take over the remaining tasks. In the next section you will see how to receive notifications when objects collide. Open VIEWCONTROLLER.M and implement the Uicollisionbehaviordelegate protocol:
- @Interface Viewcontroller ()
- @end
Or in Viewdidload, after initializing the collision behavior, set the current view controller as its proxy (delegate), the code is as follows:
- _collision.collisiondelegate = self;
Then, add a proxy method for the collision behavior:
- -(void) Collisionbehavior: (Uicollisionbehavior *) behavior Begancontactforitem: (ID) Item
- Withboundaryidentifier: (ID) identifier Atpoint: (Cgpoint) p {
- NSLog (@"boundary contact occurred-%@", identifier);
- }
When a collision occurs, the proxy method is called and the Debug information is printed on the console. To avoid the console's information being too messy, you can delete the debug information that was previously added to the _collision.action. Compile and run, objects collide with each other, and you will see the following information in the console:
- 2013-07-26 08:44:37.473 dynamicsplayground[18104:a0b] boundary contact Occurred-barrier
- 2013-07-26 08:44:37.689 dynamicsplayground[18104:a0b] boundary contact Occurred-barrier
- 2013-07-26 08:44:38.256 dynamicsplayground[18104:a0b] Boundary contact occurred-(null)
- 2013-07-26 08:44:38.372 dynamicsplayground[18104:a0b] Boundary contact occurred-(null)
- 2013-07-26 08:44:38.455 dynamicsplayground[18104:a0b] Boundary contact occurred-(null)
- 2013-07-26 08:44:38.489 dynamicsplayground[18104:a0b] Boundary contact occurred-(null)
- 2013-07-26 08:44:38.540 dynamicsplayground[18104:a0b] Boundary contact occurred-(null)
From the debug message, you can see that the block has touched two obstacles, which is the invisible boundary previously added. (null) refers to the boundary of the reference view. These debug messages are interesting to read (seriously), but it's even more interesting if you can trigger some visual feedback during a collision. After the code that outputs the debugging information, add the following code:
- uiview* view = (uiview*) item;
- View.backgroundcolor = [Uicolor Yellowcolor];
- [UIView animatewithduration:0.3 animations:^{
- View.backgroundcolor = [Uicolor Graycolor];
- }];
The code above will change the background color of the colliding object to yellow, then fade back to Gray. Compile and run, and look at the actual effect: every time the block collides with the boundary, it flashes yellow. The UIKit force learns to calculate and automatically set their physical properties (such as mass or elasticity coefficients) based on the bounds of the object. Next you will see how to use the Uidynamicitembehavior class to control these physical properties.
Set Object PropertiesAt the end of Viewdidload, add the following code:
- uidynamicitembehavior* itembehaviour = [[Uidynamicitembehavior alloc] initwithitems:@[square]];
- itembehaviour.elasticity = 0.6;
- [_animator Addbehavior:itembehaviour];
The above code creates an object behavior (item behavior), associates it to a block, and then adds the behavior to the animation instance (animator). The elastic coefficient attribute (elasticity) controls the elasticity of the object, taking 1.0 to represent a fully elastic collision, meaning that there is no loss of energy or speed in the collision. You just set the square to a modulus of elasticity of 0.6, which means that the block slows down every time it bounces. To compile and run the application, you will find that the current block is more resilient than before, as follows: NOTE: If you want to know how I generated a piece to show the historical position of the block, it is quite simple! I added a simple block to the action property of the behavior, adding a new block to the current view using the current square's midpoint position and transform property, with each execution five times. In the above code, you only change the elastic coefficients of the object, and then there are many other properties that can be adjusted for the object's behavior class. The following properties are available:
elasticity (elasticity coefficient)– Determines the degree of elasticity of the collision, such as the elasticity of the object during a collision.
friction (coefficient of friction)– Determines the amount of friction when sliding along the contact surface.
density (density)– Used in conjunction with size to calculate the total mass of the object. The larger the mass, the more difficult it is to accelerate or decelerate the object.
resistance (resistance)– Determines the resistance size of the linear movement, the friction coefficient is different, the number of friction system only acts on the sliding motion.
angularresistance (rotational resistance)– Determines the resistance size of the rotational motion.
allowsrotation (allow rotation)– This property is interesting, it has no corresponding model in the real physical world. Setting this property to no object will not rotate at all and will not be able to get much power.
Dynamic Add BehaviorNow the case, your application sets all the behavior of the system, and then the physical behavior of the system is handled by the mechanics engine until all objects are stationary. In the next step, you'll see how to dynamically add or remove behaviors. Open VIEWCONTROLLER.M and add the following instance variable:
- BOOL _firstcontact;
Add the following code to the end of the collision proxy method CollisionBehavior:beganContactForItem:withBoundaryIdentifier:atPoint:
- if (!_firstcontact)
- {
- _firstcontact = YES;
- uiview* square = [[UIView alloc] Initwithframe:cgrectmake (30, 0, 100, 100)];
- Square.backgroundcolor = [Uicolor Graycolor];
- [Self.view Addsubview:square];
- [_collision Additem:square];
- [_gravity Additem:square];
- uiattachmentbehavior* attach = [[Uiattachmentbehavior alloc] Initwithitem:view
- Attachedtoitem:square];
- [_animator Addbehavior:attach];
- }
The above code detects the first contact of blocks and obstructions, creates a second block and adds it to the collision and gravity behavior. In addition, a adsorption behavior is set up to achieve the effect of adding a virtual spring between two objects. Compile and run the app, when the original block hits the obstacle, you should see a new block appear, as follows: Although the two blocks appear to be connected together, you don't see a visual connection because you don't draw lines or springs on the screen.
What do you do next? Now you should know more about the core concepts of UIKit mechanics. If you are interested in learning more about UIKit mechanics, you can focus on our new book IOS 7 tutorial set. The book combines what you've learned in this article, with more in-depth content that shows how to use UIKit mechanics in real-world scenarios: Users can pull up a recipe to preview it, and when the user releases the recipe, it will fall back to the menu or docked at the top of the screen. The final product is an application with a real physical experience. I hope you enjoy this UIKit mechanics tutorial – We think it's cool and look forward to seeing your creative interactions in the app. If you have any questions or comments, please join the forum below for discussion! The complete code for the Dynamics Playground project you created in this tutorial can be found on GitHub, and each step of the compilation run corresponds to a commit.
(reprint) Uikit Mechanics Course