UIView
Every UIView subclass implements the method drawRect:, which contains the drawing code for the view.
For example, a UIButton's drawRect: method draws a rounded rectangle with a title string in the center.
Each time an instance of UIView needs to be drawn (or redrawn), the system prepares a graphics context specifically for that view. Then the context is activated, and the message drawRect: is sent to the instance of UIView that is being drawn. The graphics context's type is CGContextRef (Core Graphics Context Reference).
A graphics context also stores its drawing state, which inlcudes things like the current drawing color, coordinate system, and the current line width.
- (void)drawRect:(CGRect)rect { // What rectangle am I filling? CGRect bounds = [self bounds]; // Where is its center? CGPoint center; center.x = bounds.origin.x + bounds.size.width / 2.0; center.y = bounds.origin.y + bounds.size.height / 2.0; // From the center how far out to a corner? float maxRadius = hypot(bounds.size.width, bounds.size.height) / 2.0; // Get the context being drawn upon CGContextRef context = UIGraphicsGetCurrentContext(); // All lines will be drawn 10 points wide CGContextSetLineWidth(context, 10); // Set the stroke color to light gray [[UIColor lightGrayColor] setStroke]; // Draw concentric circles from the outside in for (float currentRadius = maxRadius; currentRadius > 0; currentRadius -= 20) { CGContextAddArc(context, center.x, center.y, currentRadius, 0.0, M_PI * 2.0, YES); CGContextStrokePath(context); } }
Notice you are passed a CGRect structure. This is the rectangle that needs to be redrawn, sometimes called the dirty rectangle. A CGRect structure contains the members origin and size. The origin is of type CGPoint and contains two float members: x and y. The size if of type CGSize and also has two float members: width and height.
Remember that a structure is not an objective-c object, so you can't send it messages.
Instantiating a UIView
There are two ways to create an instance of your view:
- visually choose and position the view while editing the story board file
- create it programmatically with alloc and initWithFrame: and make it a subview of the window
#import <UIKit/UIKit.h> //this is a "forward declaration" @class HypnosisView; ... HypnosisView *view;
This is a forward declaration for the class HypnosisView.When you forward declare a class, you aren't going as far as importing the header file; You are just informing HypnosisterAppDelegate.h of the class HypnosisView so the compiler can validate it. Forward declaring a class saves time when compiling - especially with large projects.
- (BOOL) application: (UIApplication *) application didFinishLaunchingWithOptions:(NSDictionary *) launchOptions { // Make a CGRect that is the size of the window CGRect wholeWindow = [[self window] bounds]; // Create an instance of HypnosisView that is the same sizeas the window view = [[HypnosisView alloc] initWithFrame:wholeWindow]; // Set the background color of that view to "clear" [view setBackgroundColor:[UIColor clearColor]]; // Add the view to the view hierarchy so that it appears onthe window [[self window] addSubView:view]; [[self window] makeKeyAndVisible]; return YES; }
Neither HypnosisterAppDelegate nor the window will ever get deallocated because they exist the entire time the application is running.
UIScrollView
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:wholeWindow]; [[self window] addSubView:scrollView]; [scrollView setContentSize:size]; //center it in the scroll view CGPoint offset; offset.x = wholeWindow.size.width*0.5; offset.y = wholeWindow.size.height*0.5; [scrollView setContentOffset:offset]; //create the view view = [[HypnosisView alloc] initWithFrame:reallyBigRect]; [scrollView addSubView:view]; ... }
Zooming
To add zooming, you need to give the scroll view a delegate
@interface HypnosisAppDelegate: NSObject <UIApplicationDelegate, UIScrollViewDelegate>
and in didFinishLaunchingWithOptions...
//enable zooming [scrollView setMinimumZoomScale:0.5]; [scrollView setMaximumZoomScale:5]; [scrollView setDelegate:self];
in the same file, implement the delegate method viewForZoomingInScrollView:
- (UIView *) viewForZoomingInScrollView: (UIScrollView *)scrollView { return view; }
You probably don't want to see the time or your remaining battery charge. So, you are going to hide the status bar before you make the window visible.
add a line near the end of application:didFinishLaunchingWithOptions:
[[UIApplication sharedApplication] setStatusBarHidden:YES withAnimation:UIStatusBarAnimationFade]; [[self window] makeKeyAndVisible]; return YES;
You can also info property list with hidden status bar in project target setting.
Status bar is initially hidden | YES |
Retain Cycles
A view hierarchy is made up of many parent-child relationships. When we talk about view hierarchies, we call parents superviews and their children subviews.
The solution is simple: children should never retain their parents. When you adhere to this rule, deallocating a parent object appropriately release its child objects. If the parent is the only owner of its children, then these child objects are deallocated.
Redrawing Views
When a UIView instance is sent the message setNeesDisplay, the view is marked for re-display. When a view has marked itself for re-display, it is not immediately redrawn, instead, it is added to a list of views that needs updating. Your application is a giant infinite loop called the run loop, The run loop's job is to check for input(a touch,core location updates, data coming in through a network interface, etc. ) and then find the appropriate handlers for that event.