iOS: Non-square hit areas for buttons - cocoa-touch

I need to make some triangular buttons that overlap each other.
While UIButtons can take transparent images as backgrounds, and UIControls can have custom views, the hit area of these is always square. How can I create a triangular hitarea for my buttons?
I come from a FLash background so I would normally create a hitarea for my view, but I don't believe I can do this in Cocoa.
Any tips?

You can achieve this by subclassing UIButton and providing your own:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
// return YES if point is inside the receiver’s bounds; otherwise, NO.
}
Apple's UIView Documentation provides the details, such as confirming that point is already in the receiver's coordinate system.

Related

How to define a tappable area defined by coordinates

I am writing a small iPad application that draws a shape from a list of coordinates. I would like to tap anywhere inside the shape and have some action occur (i.e. NSLog proving it worked).
Does anyone know how to create a tappable area that is defined by a list of coordinates?
The shape is being drawn on top of a MKMapView.
My approach would be:
Have the points that demark the shape live within a subclass of UIView. Override pointInside:withEvent: for that class. Then look at How can I determine whether a 2D Point is within a Polygon? and use your new knowledge to implement pointInside:withEvent:
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
//Left as homework
}
You can use a regular tap gesture recognizer with this :)
Only instances of UIView are tappable, and their area is defined by their rectangular property frame. In principle it would be possible (for very simple and specific shapes) to approximate the area defined by coordinates by multiple UIViews, but this is probably not what you want.

Zooming the whole screen with UIPinchGestureRecognizer

I was trying to add a pinch gesture zoom-in zoom-out feature to a UITextView and then I decided that adding a pinch gesture which zooms the whole screen, covering the whole view hierarchy is a better and more general solution. But I am confused about how to do it: Should I change the frame sizes of all UIView objects or should I somehow scale them? What is the most correct way to do it?
Put all your views in a UIScrollView and then return the view in this delegate method of UIScrollView
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView

What is the need to subclass a UIView instead of creating our own view and adding it to

Why would anyone subclass a UIView instead of creating our own custom view like this:
CGRect labelRect = CGRectMake(20,20,20,20); //Frame to contain the current view
UILabel *label = [[UILabel alloc] initWithFrame:labelRect];
label.text = #”This is a custom view with out subclassing UIView”
[self.view addSubView:label];
Is don't see any tradeoffs or advantages? Am I missing something?
In a lot of cases where you are just rearranging text, and utilizing some of the built in buttons, labels, etc; it is not necessary to subclass a UIView and can instead be done programmatically through a UIViewController or added to a storyboard/nib file. However, a great example of when and why you would want to subclass a UIView is if you wanted to add some custom touch behavior or drawing to the view that you are using.
So the way I see it, the main considerations in subclassing a UIView comes down to the following:
1) Touch interaction,
2) Custom Drawing
*Note that there may be other reasons you may want to create a view subclass simply from a program organization perspective, but these are the main three that the behavior cannot be delegate to another function, or added directly to the UIView. For example, support for general animations and background image manipulation can all be handled without subclassing UIView.
Touch Interaction - Touch interaction with a UIView can be handled in a couple different ways, you can interact directly with the touch events, or add a gesture recognizer to the UIView. In many cases, you can accomplish everything that you need to through a gesture recognizer (or custom gesture recognizer), rather than attempting to look at the individual touches that are occurring on your view, but if you wanted/needed to access the raw touch events, you would override the following functions to implement the desired behavior:
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
-(void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
-(void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Custom Drawing - In a UIView you can do basic 2D drawing through Quartz/Core Graphics. One example is that if you were doing a graphing application and wanted your UIView to draw the actual lines for you within its context on the screed. To accomplish this, you would override the drawRect:(CGRect)rect function and list any custom drawing here.
That being said, this is by no means the only way that you could draw a line, and could complete this task without performing custom drawing. For example, that same line, or set of lines, could be drawn into an image buffer by your UIViewController, and then displayed as the background of your view or a subview accordingly. When doing that however, the UIView loses the knowledge of the line on screen, and touch interaction directly with that line becomes more challenging. In the end, keep in mind that there are creative ways to get around subclassing just to display custom drawing, but sometimes from a program design perspective it may make more sense to just create a UIView subclass.
These are by no means the only reasons that you would want to subclass a UIView, but have been the 2 biggest reasons that I have seen used for creating a subclass of UIView. I would suggest that you look at UIView class reference or View Programming Guide for iOS for more details.
for the same reason UIView is subclassed with controls and text layout views in UIKit: because the existing implementations don't do everything, and custom implementation or members are needed (at times).
however, you might subclass UILabel in this case, if you needed to do something specific, or reuse an implementation and subclassing were a good solution.
I would say custom behaviors and custom layouts. Handling resizing beyond what is available to auto-resizing mask or smart loading subviews is more difficult to do from an outside controller.
I've created a custom image view that has a scroll view to see a long list of images in a three column display. It internally manages the images, so it only loads views for images that are seen on the screen.
Could you imagine having to build a UITableView by hand compositing the scroll view with cells, managing the queue of cell views, having to smart load the cells only when they would appear, and handling all the tap, swipe, and pan gestures every time you needed a screen with a tabular look?

How to implement a custom Focus Ring in drawRect for NSTextField or NSTextVew

I want to draw a custom focus ring for my NSTextView subclass (which doesn't have a focus ring by default). I managed to implement it by overriding the parent NSScrollView drawRect and adding this code:
- (void)drawRect:(NSRect)dirtyRect {
if (focused) {
NSSetFocusRingStyle(NSFocusRingOnly);
NSRectFill(dirtyRect);
}
[super drawRect:dirtyRect];
}
However, I want to draw my own, custom focus ring. I have searched and searched for examples of this, and tried messing around and writing it myself, to no avail. The biggest issue I have is the fact that it will get cropped to the NSScrollView/NSTextView frame, no matter how I do it.
Thanks.
Updating this answer for 10.7+:
Now you should override drawFocusRingMask to render (simply drawing a shape; the system will take care of color/style), and override focusRingMaskBounds to hint at its boundaries. Also, call noteFocusRingMaskChanged if you change the shape in some way that the system could not figure out on its own.
(Below is the previous answer, requiring older APIs:)
In the Carbon framework there are HIThemeBeginFocus() and HIThemeEndFocus(), which allow you to cause any series of drawings (such as a rectangle or shape) to have an automatic "focused" appearance. Requires Mac OS X 10.5 or later.
This uses Core Graphics directly. To find the CG context from a drawRect: method in Cocoa, you'd do something like:
NSGraphicsContext* contextMgr = [NSGraphicsContext currentContext];
CGContextRef drawingContext = (CGContextRef)[contextMgr graphicsPort];
As far as avoiding clipping, one option is to use a parent view (such as an NSBox that has no border) to give extra padding. Perform the custom drawing at an inset location in the parent view that won't be clipped; in other words, give the illusion that the view is a bit smaller than its actual rectangle.

How do I rotate the contents of a UIWebView?

I have made a very simple web browser app using a web view. Now I need to get the app so that when the iPhone is rotated, the text of the page is rotated as well.
How do I do this?
I am very confused by the auto-resize dialog, so it is possible I have done something wrong there.
Any help would be appreciated!
I think you sholud rotate UIWebView widget, not its contents. Contents should rotate as well. To support rotating add following code to your view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Rotated widget might look different then expected. Adjust struts and springs in Interface Builder.
I think you need to give us some sample code in order to determine what goes wrong. It is as Jacek says, the only think you should need to do is to support auto rotation on the UIWebView itself. The content should be rotated automatically.
I think you are confused by device orientation and view frame.
In most cases UIViews do change with respect to the orientation change. But to clarify - it is not because of the orientation change, but the layout change.
Only UIViewControllers need to consider device orientation - UIViews do NOT. When the device orientation changes, the UIViewController captures the event from its instance methods:
– willRotateToInterfaceOrientation:duration:
– willAnimateRotationToInterfaceOrientation:duration:
– didRotateFromInterfaceOrientation:
The UIViewController then re-layout the views - leading to reframing of the UIViews. In many cases, iOS can helps you in simplifying the relayout process by setting the UIViewAutoresizeMask. For example:
myWebview.autoresizeMask = UIVIewAutoresizeMaskFlexibleHeight | UIVIewAutoresizeMaskFlexibleHeight;
implies that when webview's superview changed its bounds, the webview will change accordingly.
As a summary, UIView only takes care of its frame / bounds etc.