UIView orientation is incorrect after the first draw - uiview

I have a view controller that alternates 2 views on every touch. Each of the views overrides the drawRect function.
It works when the iPad is in portrait position but for landscape position, the view is shown in the right orientation only once. After that they always appear in portrait orientation.
What's wrong?
In ViewController:
- (void)loadView
{
v= [[View2 alloc] init];
self.view =v;
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
x++;
if (x%2==0)
{
v= [[View2 alloc] init];
}
else {
v=[[View3 alloc] init];
}
self.view = v;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}

I fixed the problem. It's simple and makes sense. Basically the problem is that only the FIRST view receives orientation events. When changing the view, event handlers must be hooked up again so that the new view can detect the orientation.
An easy fix is to create a dummy view as the parent and add view1 and view2 as subviews. My new controller code is as follow:
- (void)loadView
{
self.view =[[UIView alloc] init];
v= [[View2 alloc] init];
[self.view addSubview:v];
}
-(void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
x++;
if (v!=NULL)
[v removeFromSuperview];
if (x%2==0)
{
v= [[View2 alloc] init];
}
else {
v=[[View3 alloc] init];
}
[self.view addSubview:v];
}

Related

iOS animation presentationLayer hitTest does not seem to work as expected

I'm trying to add touch events to small view blocks drifting from screen right side to left side. The problem is layer.presentationLayer hitTest:point method returns nothing, even if I do actually tap within the bounds of the view block.
Finally, I find a workaround. But, two questions still confuse me.
The converted point does not seem to be right, how to properly use presentationLayer hitTest?
In my code snippet, UIViewAnimationOptionAllowUserInteraction is not set, why I can handle touch event anyway?
The following are the code, any help will be appreciated
viewDidLoad
CGRect bounds = [[UIScreen mainScreen] bounds];
for (int i = 0; i < 15; i++) {
ViewBlock *view = [[ViewBlock alloc] initWithFrame:CGRectMake(bounds.size.width, 200, 40, 40)];
[self.view addSubview:view];
[UIView animateWithDuration:20 delay:i * 21 options:UIViewAnimationOptionCurveLinear animations:^{
view.frame = CGRectMake(-40, 200, 40, 40);
} completion:^(BOOL finished) {
[view removeFromSuperview];
}];
}
ViewBlock.m
#implementation ViewBlock
- (instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event{
CALayer *presentationLayer = self.layer.presentationLayer; //Do NOT modify presentationLayer
if (presentationLayer) {
CGPoint layer_point = [presentationLayer convertPoint:point fromLayer:self.layer.modelLayer];
if ([presentationLayer hitTest:point]) {
return self; //not worked
}
if ([presentationLayer hitTest:layer_point]) {
return self; //not worked either
}
if (CGRectContainsPoint(presentationLayer.bounds, layer_point)) {
return self; //the workaround
}
}
return [super hitTest:point withEvent:event];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[super touchesBegan:touches withEvent:event];
NSLog(#"touches began");
}
- (void)didMoveToSuperview{
[super didMoveToSuperview];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapTouched:)]
;
tap.cancelsTouchesInView = NO;
[self addGestureRecognizer:tap];
}
- (void)tapTouched:(UITapGestureRecognizer *)sender{
NSLog(#"touches gesture");
}
#end
Q1: The converted point does not seem to be right, how to properly use presentationLayer hitTest?
What you are missing is the parameter of hitTest: should be in the coordinate system of the receiver's super layer, so the code should be:
CGPoint superLayerP = [presentationLayer.superlayer convertPoint:layer_point fromLayer:presentationLayer];
if ([presentationLayer hitTest:superLayerP]) {
return self;
}
Q2:In my code snippet, UIViewAnimationOptionAllowUserInteraction is not set, why I can handle touch event anyway
I think you can get touch event because you override - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event and return self as result, so the event will pass to it. By default(If you don't override the hitTest:withEvent: method), you won't get touch event.
If you set UIViewAnimationOptionAllowUserInteraction option, you can get touch event on the view's final frame (CGRectMake(-40, 200, 40, 40)), but in this example, the final frame is off the screen, you can set it to CGRectMake(0, 200, 40, 40) and have a test.

IOS:Remove view when clicked in background

I am doing app related to custom alert. Written code for displaying alert view with transparent background when i click ok button on alert-view an alert disappears.
I need help for same thing happen while i touch the transparent view also my code is a below
:
- (void)didCustomPopUpAlertLoad:(UIView *)parentView andtitle:(NSString *)strTitle {
[self setRootView:parentView];
self.lblAlertMessage.text = strTitle;
//Add alertview into transparent view to hide parent view interaction
UIView *transparentView = [[UIView alloc] initWithFrame:parentView.bounds];
[transparentView setBackgroundColor:[UIColor clearColor]];
[transparentView addSubview:self];
float x = (int)(transparentView.bounds.size.width - self.bounds.size.width)>>1;
float y = (int)(transparentView.bounds.size.height - self.bounds.size.height)>>2;
[self setFrame:CGRectMake(x, y+62, self.bounds.size.width, self.bounds.size.height)];
// [self setFrame:CGRectMake(x+10, y+62, self.bounds.size.width, self.bounds.size.height)];
[self.window addSubview:transparentView];
[self.window makeKeyAndVisible];
[[transparentView subviews]
makeObjectsPerformSelector:#selector(setUserInteractionEnabled:)
withObject:[NSNumber numberWithBool:FALSE]];
}
-(void)didCustomPopUpUnload{
[self.superview removeFromSuperview];
// Set up the fade-in animation
self.window = nil;
}
-(IBAction)didActionOkAlertPopUp:(id)sender{
[self didCustomPopUpUnload];
}
Create a custom transparent view then override this method
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
Add your alert view into this view and use this view as full screen view.
You can use event param to calculate to know user touch inside or outside of alertview.

Get touches when userinteractionenabled is NO

There seems to be an answer, but when I try the method provided, it just doesn't work!
#interface MyView : UIView
#end
#implementation MyView
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
UIView *hitView = [super hitTest:point withEvent:event];
NSLog(#"hitView:%#", hitView);
if (hitView == self) {
return nil;
}
return hitView;
}
- (void)testUserInteraction
{
UIViewController *vc = [[UIViewController alloc] init];
self.window.rootViewController = vc;
MyView *myView = [[MyView alloc] initWithFrame:CGRectMake(0, 0, 320, 568)];
myView.userInteractionEnabled = NO;
[vc.view addSubview:myView];
UIView *subView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
subView.backgroundColor = [UIColor orangeColor];
[myView addSubview:subView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped)];
[subView addGestureRecognizer:tap];
}
When myView.userInteractionEnabled = YES; everything works fine. but when myView.userInteractionEnabled = NO; wherever I tapped the screen, it just output hitView:(null)
So is this method no longer working, or did I miss something?
yes, because you disabled user interaction.
When you call super-hittest,the return from super-hittest depends on userInteraction property.
If it is enabled then only, it returns either itself or some subView.
If not enabled, it will always return nil.
If I understand correctly, you want to know when a touch happens inside your custom UIView, while user interaction on the view is disabled. If this is so, throw the following inside the - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event method. This condition should read only touch events inside your custom view
if ([self pointInside:point withEvent:event]) {
NSLog(#"TOUCH IS INSIDE THE VIEW");
}

Passthrough Touch Event

What's the best way to passthrough a touch event to all subviews?
ViewController -> view -> (subview1, subview2)
I would like subview1 & subview2 to both respond to the touch event.
Set the tags on the subviews equal to noticable tags. Then do a tree search through the views looking for those tags. Unfortunately there is not really a nice way to do this without subclassing. If you were willing to subclass then you would subclass view and then upon touch throw a NSNotification that all other views of that same subclass would listen for.
In the parent's touch handler, you can iterate over the subviews of that view and call the same handler:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
for(UIView *subview in [self.view subviews]) {
[subview touchesBegan:touches withEvent:event];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
for(UIView *subview in [self.view subviews]) {
[subview touchesMoved:touches withEvent:event];
}
}
Or if you needed to identify specific subviews, you could assign integer Tags to the subviews to identify them later:
- (void)loadView {
UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(10,10,10,10)];
view1.tag = 100;
[self.view addSubview:view1];
UIView *view2 = [[UIView alloc] initWithFrame:CGRectMake(20,20,20,20)];
view2.tag = 200;
[self.view addSubview:view2];
}
Then later in the ViewController method invoked by the touch event
- (void)touchEventResponder {
UIView *view1 = [self.view viewWithTag:100];
// Do work with view1
UIView *view2 = [self.view viewWithTag:200];
// Do work with view2
}

How to implement "finger-tracing" layer in app like the iOS Simulator

Now that our app has shipped, our person in charge of training has a request:
"The app is cool, but we need a mode showing the movement of the fingers, like how gestures show up in the Simulator or iPad's laser pointer mode in Keynote [so we can capture films]."
This is an app that uses a UITabBarController with xibs for each tab (4). This is a pretty complicated app with interacting gesture recognizers and I was having "first, do no harm" nightmares...
I tried adding a simple "global UIView" above the tab controller using the scheme here: http://www.james-vandyne.com/creating-universal-overlays-using-uiviews-on-ios/
My "PresentationView" attempt:
in my app delegate:
UIWindow *mainWindow = [[UIApplication sharedApplication] keyWindow];
[self setPresentationView:[[PresentationView alloc] initWithFrame: [mainWindow frame]]];
[mainWindow insertSubview:presentationView aboveSubview:mainWindow];
The class:
// PresentationView.h
#import <UIKit/UIKit.h>
#interface PresentationView : UIView
{
NSMutableDictionary *touchesInView;
}
#property (nonatomic, retain) NSMutableDictionary *touchesInView;
#end
#import "PresentationView.h"
#import "TouchPoint.h"
#implementation PresentationView
#synthesize touchesInView;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[ self setAlpha:0.1];
[self setBackgroundColor:[UIColor whiteColor]];
[self setUserInteractionEnabled:YES];
[self setMultipleTouchEnabled:YES];
touchesInView = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void) dealloc
{
[touchesInView release];
touchesInView = nil;
[super dealloc];
}
- (void)drawRect:(CGRect)rect
{
if ([touchesInView count] < 1)
return;
CGContextRef context = UIGraphicsGetCurrentContext();
NSEnumerator *touchEnum = [touchesInView objectEnumerator];
TouchPoint *nextTouch;
float rad = 24;
while (nextTouch = [touchEnum nextObject]) {
[[ UIColor redColor] set];
CGContextAddArc(context, nextTouch.current.x, nextTouch.current.y, rad, 0.0, M_PI*2, 1);
CGContextFillPath(context);
CGContextStrokePath(context);
}
}
- (UIView *) hitTest:(CGPoint)point withEvent:(UIEvent *)event
{
[self setNeedsDisplay];
return [[[[[UIApplication sharedApplication] keyWindow] subviews] objectAtIndex:0] hitTest:point withEvent:event];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for( UITouch *t in touches )
{
NSValue *key = [NSValue valueWithPointer:t];
CGPoint loc = [t locationInView:self];
TouchPoint *newTouch = [[TouchPoint alloc] init];
[newTouch setBegan:loc];
[newTouch setCurrent:loc];
[newTouch setTapCount:[t tapCount]];
[touchesInView setObject:newTouch forKey:key];
[newTouch release];
}
[self setNeedsDisplay];
[[[[[UIApplication sharedApplication] keyWindow] subviews] objectAtIndex:0] touchesBegan:touches withEvent:event];
}
// More touch methods- all ignored
This kinda works- the view sits atop all the other views, and hitTest:withEvent: fires, and I was successfully drawing a red circle at the hit point in drawRect:.
But it breaks down when I try to track touches- touchesBegan/Moved/Ended don't fire. (Yes, I have turned on UserInteractionEnabled and setMultipleTouchEnabled for this global view). Fortunately, I haven't screwed up gesture recognition on anything below- the other views are getting events, and the app is behaving normally under this global view.
Normally I use IB to build/locate views and don't build them in code from scratch, so it's very possible there's (a?/several?) simple UIView properties I'm not setting up right... but I can't see it. Or am I going to get myself in trouble with this, trying to forward events down the chain?
There's a good framework called FingerTips that does this.
It's also easy to implement and it only shows the circles if there is an external display so you are presenting it.

Resources