This recipe is written for the Xamarin iOS 7 Cook-off. The source code can be found here.
iOS 7 introduces a lot of new features. We will explore the gravity and collision behaviors the OS provides natively with its newest version, building a kaleidoscope image gallery.
Start by creating a new Xamarin.iOS project. Go to File > New > Solution. Select C# > iOS from the sidebar and then Single View Application. Name your application and Xamarin Studio will create for us a ViewController.
Continue by adding an Images folder, right clicking the project in the Solution pad and selecting Add > New Folder. Continue by adding in it the photos we want to display.
Let’s open the ViewController that was created and start creating out gallery. In the ViewDidLoad method, let’s create the animator which will add the physics behavior on the images.
Animator = new UIDynamicAnimator (View);
Next, we will create a container for the images. A scroll view will allow us to hold all the images and browse through them. Init the scroll view and call the SizeToFit method to make sure the size will increase if there are more images than the (initial) view frame can contain. To enable scrolling behaviour, set the content size smaller than the actual scroll’s size.
imagesContainer = new UIScrollView (View.Bounds); imagesContainer.SizeToFit (); imagesContainer.ContentSize = new SizeF (View.Bounds.Width, View.Bounds.Height); imagesContainer.AutoresizingMask = UIViewAutoresizing.All; View.Add (imagesContainer);
Let’s now add the photos from the Images Folder to the container we created.
string[] filePaths = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory + "/Images", "*.jpg"); foreach (string path in filePaths) { UIImageView imgView = new UIImageView (); imgView.Image = new UIImage (path); imgView.Frame = new RectangleF (imagePositionX, imagePositionY, imageWidth, imageHeight); AddTapRecognizer (imgView); imagesContainer.Add (imgView); }
We use the image’s frame to position it inside the view. The first image with have the X and Y coords equal 0 and the following images will come right after. The X and Y coords will be increased each iteration. If the X coord would position an image outside the visible area, it will be reset, and the Y increased with the height of the image.
if (imagePositionX + imageWidth * 2 < View.Bounds.Width) { imagePositionX = imagePositionX + imageWidth; } else { imagePositionX = 0; }
After an image is set in place, let’s add a behavior. Remember to set the UserInteractionEnabled to true, otherwise the images won’t register the tap event, which will not be fired.
tap = new UITapGestureRecognizer (() => { AnimateImageShow (imgView); }); imgView.AddGestureRecognizer (tap); imgView.UserInteractionEnabled = true;
Whenever we tap an image, let’s increase its size and make the other images drop to the bottom. Remove all other events registered with the animator (otherwise we will have a memory leak!) and create a UISnapBehavior. This make the view passed in as a parameter move to the point specified as the second parameter. Let’s also register an action with this event, which will size the image by a specified factor.
//Remove hanging events Animator.RemoveAllBehaviors (); //Center and enlarge the selected image snap = new UISnapBehavior (imgView, imagesContainer.Center); snap.Action = () => { ScaleImage (imgView, 3); }; Animator.AddBehavior (snap); //Drop all other imagesDropImages(imagesContainer.Subviews.Where (x => x != imgView).ToArray());
To make the other images drop to the bottom, just add them into an array using Linq. We can use this array for creating the two behaviors we want: the gravity, which will make the drop, and the collision, which will make them stack on top of each other. For the collision, set the TranslatesReferenceBoundsIntoBoundary to true to make the images collide with the view’s bounds and the CollisionMode to Everything to enable them to collide with each other and stack on top of one another. Otherwise, they would all hit the bottom of the view.
var gravityBehavior = new UIGravityBehavior (imgsView); var collisionBehavior = new UICollisionBehavior (imgsView) { TranslatesReferenceBoundsIntoBoundary = true, CollisionMode = UICollisionBehaviorMode.Everything }; Animator.AddBehaviors (gravityBehavior, collisionBehavior);
We have completed the basic functionality of a gallery. Each time an image is tapped, it is brought in front and scaled, while the other images are realistically dropped to the bottom of the screen, creating a kaleidoscope effect with each new tap.
There are many more features that iOS 7 offers, not only in terms of physics behaviors and for more information, check out the Xamarin article that covers the most important new features.