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 uponCGContextRef context = UIGraphicsGetCurrentContext();// All lines will be drawn 10 points wideCGContextSetLineWidth(context, 10);// Set the stroke color to light gray[[UIColor lightGrayColor] setStroke];// Draw concentric circles from the outside infor (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//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 *) applicationdidFinishLaunchingWithOptions:(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 viewCGPoint offset;offset.x = wholeWindow.size.width*0.5;offset.y = wholeWindow.size.height*0.5;[scrollView setContentOffset:offset];//create the viewview = [[HypnosisView alloc] initWithFrame:reallyBigRect];[scrollView addSubView:view];...}
Zooming
To add zooming, you need to give the scroll view a delegate
@interface HypnosisAppDelegate: NSObject
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.