UIView Basics - UBogun/Xojo-iosLib GitHub Wiki
In iOS, every control you see on the device is a subclass based on UIView (AppleView in iOSLib terms). A UIView is simply a rectangular piece of screen space. The Xojo analogy is a control, or rather iOSControl for iOS. That’s why you can access AppleView features on everything visible – the controlextension adds the UIView features transparently to everything based on iOSControl.
If you take a closer look into the AppleView class, you will notice that it is much more than just a rectangular array of pixels. It is indeed a container that takes care about the visualization of its defined area, and its final look can be the result of computations based on its contents. The number of features can be quite overwhelming. That's why I try to break it up a bit into smaller pieces.
Contents | where to find |
---|---|
Alpha | This document |
Animatable properties | This document |
Animations | Part II |
BackgroundColor | This document |
Constructor | This document |
Transitions | Part III |
Positioning | This document |
Construction time
The constructor takes a NSRectangle as input parameter. Makes sense, because a rectangular shape needs to have rectangular dimensions. A line of code like
Dim myNewView As New AppleView (FoundationFramework.NSMakeRect (0,0,100,100))
would create a square at the upper left of the screen (if you add it to the screen view, that is). To be honest, you will rarely do so but use one of the specialized UIView subclasses. Anyway, they may have additional parameters in their constructors, but you will always have to define their rectangle.
In the majority of cases, you will place the control on the Xojo IDE workspace visually.
Basic properties
Positioning the view
Frame The rectangle you pass creates the frame property of the view. You can change this property (or its derived properties x, y, width and height) later to move the view freely around the device screen (but should be aware that layoutconstraints you attached to the view may interfere with your wish). Additionally, construction of a view defines its
Bounds property as well. The bounds are simply the coordinates of the view in its own coordinate system. So for a UIView with a Frame property of (50,50,100,100), its Bounds would be (0,0,100,100).
What is that good for? If you want to manipulate the position or size of a view from the outside – from its parent –, use its Frame property. If you are addressing it from inside, like to position another subview, use the Bounds property.
Another way to manipulate the position of a view is its
Center property. Sometimes it's useful to just use a point instead of the upper left corner. If you want to do this, Center is the property to address. It defines, as you might have guessed, the position of the view's center relative to its Superview.
These three properties are connected. If you change the view's center, you change its frame respectively.
You’ll find some good illustrations at the beginning of these lecture slides.
Frame, View and Center properties for a view with a width of 700 and a height of 500 points positioned at x = 50, y = 350 in its superview.
… and animating it.
Sure: iOS could exist (and probably work even a bit faster) without all those nice animations. iOSViews and alerts could just appear, an eBook reader could flip pages without animation. But those features tend to make the GUI feel more organic, and the user being in most cases a human likes that in general. That’s why the UIView class comes with an arsenal of different animation methods, and subclasses often add even more to your toolbox.
These animations take a lot of input parameters and are therefore not the easiest to handle. And most of all, you cannot animate each property smoothly. You will find which ones you can use by looking at Apple’s developers docs which list the animatable properties on the top of each view subclass if it has such.
Animation is a rather complex topic. That's why you find it in a separate chapter.
The other animatable properties of UIView are:
Transparency is a feature that's often used in modern GUIs. You can add a touch of it if you change the view’s
Alpha value. Like most Apple properties that have a variable property it takes a Double value between 0.0 and 1.0, with 0 meaning totally transparent and 1 fully opaque.
BackgroundColor should be self-explaining. If your view isn’t filled completely with an image or alike, the rest will take on this color.
Transform is a rather complicated thing. If you’ve looked at Apple’s docs, you might have noticed the UIView class has no angle property, limited means of scaling and the like. Nonetheless, many apps make use of transformational features. The clue behind this is the Transform property of type CGAffineTransform.
CGAffineTransform is a 3 x 3 matrix of Double values, of which 7 values can be modified. Its values are used to perform a matrix multiplication on the content of the view before it is displayed – which means, the contents stay unchanged but before they are drawn to the screen,they are scaled, rotated or moved depending on the values of the matrix. If you are interested in the theories behind this property, here's an article about the basic theory and how it is implemented into the CoreFoundation framework.
But you don’t have to. What you need to know:
- A Transform property works nondestructive. The view's content is not changed, only the way it is painted to the screen. When you reset the transform property to its default values, the view draws in its originals state again.
- CGAffineTransform offers methods to mimic properties like an Angle property and methods to mimic modifications like a Rotate method would, which means modifying the existing property without resetting to the original state before.
- The name CGAffineTransform implies that the structure belongs to the Core Graphics framework, which means that it uses hardware-accelerated optimized methods to perform its operations. If you need to scale a graphics fast, it would be wise to test if using the SetScale(xScale, yScale) convenience method isn't much faster than a manual redraw. (In fact, it is by numbers! Test it yourself!)
Speaking about convenience methods. Don't worry about having to use the rather clumsy CGAffineTransform methods. I have added instance methods and properties that do the handling for you. Let's test them before I end up being tl;dr for anyone.
Some Transform fun (and Autolayout complexities)
- Start a new Xojo iOS project and copy iOSLib into it.
- Put a Xojo canvas on it, resize it to fill almost the entire iOSView, just leave a bit of space at the bottom to place a button there.
- Give the button the caption "Transform"
- Rightclick on the canvas, select "Add Event Handler" and select the paint event.
- Now we’re gonna use the CGContext object to draw a bit into it. Enter the following:
dim myG as new AppleCGContext(g)
dim grad as new AppleCGGradient(&c99999900, &c5D81D300, &c62003A00, &cFF000000, &cFFFF6600)
myg.DrawLinearGradient (grad, FoundationFrameWork.NSMakePoint(0,0), FoundationFrameWork.NSMakePoint(me.Width, me.Height))
- Add an Action event handler to the button and fill it with the following lines:
dim angle as double = 30
Canvas1.Rotate (angle.DegreeToRadian)
What did we do here? The paint event (DrawRect in Apple’s words) fires whenever the view needs to be redrawn. This doesn’t mean it fires 60 times a second or so, depending on your screen refresh rate. It only runs when the system signals it needs to be refreshed – like when after a resize, when it is overlayed by another view or the cloaking view disappears – or when you invalidate the view.
In the first line, we create the CGContext item, the iOS representative of the iOSGraphics item Xojo presents us in the event.
Next we define a CGGradient object over a few colors, which in the last line is drawn from the upper left corner to the lower right of the view.
When the button is tapped, the view should be rotated 30 degrees clockwise. Give it a try and hit Run.
You should now see an iOSView with a nice gradient, something like this:
Now tap the button. What’s this? Very probably your view has changed its form, is only partially visible, maybe it simply disappeared. How come?
A view comes never alone
Remember I talked about autolayout influencing the positioning of views in animations? It’s not only true for animations, it’s true for everything that has to do with the view’s Frame, Bounds or Center property. On a technical base we did not do so, we only changed the Transform property. But Transform calculates the final visual positioning of the view and takes the layoutconstraints into accounts.
These constraints define the relation between different anchors of two views. By placing the Canvas on the iOSView, we established a relationship between both. The canvas has become a child of the iOSView. In Apple’s terms that sentence reads as
The canvas as a UIView has become a subview of the iOSView (which is another instance of a class based on UIView).
So what’s a subview?
- Just like you can add another layer to a Photoshop image, you can attach a view to another view. The containing view becomes the superview, its child a subview.
- Just like you can add even more layers to a Photoshop image, you can add an arbitrary number of subviews to another view which will all become members of the same view hierarchy.
- And just like you can move layers around, bring them to the top or send them to the back, delete some or make them invisible in Photoshop, you can do so with the subview in a view hierarchy.
We will play with some subview features in a later article. For now it's enough to know that as long as you want it to be shown on screen,
Every view has at least one superview.
The basic UIView in iOS is Called UIWindow – in iOSLib AppleWindow of course –, and it is the view to which iOSViews attach, these themselves carrying different subclasses of UIViews in Form of controls.
With that in mind, it becomes clear what happened when you inspect the Auto-Layout properties of your canvas in Xojo. On tapping the Transform button, the visual display of the gradient view was rotated (it was not repainted! When you put a breakpoint on some line in the canvas’ paint event and run the project, you will see that the paint event is only called once!). But after the rotation the layoutconstraints were calculated, and depending on how you or Xojo set them the resulting image may appear distorted or it gets completely out of sight because the constraints calculate the now rotated anchors of the gradient view to a position outside of the display.
So lets get into Inspector, click on the canvas, delete all the constraints and add new ones like these:
Feel free to change the height or finetune the center values. Just makes it so the view’s size is fixed and its center allows for a nice rotation.
Now run the project again. Much better, right? Your gradient square should now rotate by 30 degrees on every button tap and always stay visible and at the same size.
Having said that, lets try a few more transform methods. Jump to the button action event handler and replace the last line by
Canvas1.scale (0.95)
Et voilà: The shrinking gradientrectangle, again computed in a fraction of the time it would need to refresh the canvas via its paint event.
Another transform feature is translate – move by –, so let's give it a try too:
Canvas1.Translate (5,5)
and the rectangle steps slowly out of the iOSView.
In the same manner, the methods SetScale(x, opt. y), SetRotation(Angle) and SetTranslation(x, y) perform a translation on the original view, not on the transformed view. To get a feeling for this, try the event handler with two of the first methods (like Rotate and then Scale) and then with SetRotation and SetScale.
If you want to reset the view, use
Canvas1.RemoveTransformation
which is a shortcut for
Canvas1.Transform = CGAffineTransform.Identity
And to invert a transformation, like a rotation back to original, use
Canvas1.InvertTransformation
That concludes the first part of UIView basics – and we only discussed the animatable properties. Now imagine you could use the highly optimized features of these properties for smooth animations and transitions, only defining some parameters and the system will do the rest, on the GPU, in a background thread. Which is exactly what the next part of the series does cover.