How to detect the location of taps in ios 7 - objective-c

I have a view controller with half table view (bottom, 320x289)and half map view (top, 320,289). How can I detect location of tap?
Currently my code for the tap looks like this - when tapping, it hides the navigation bar so that the map gets some extra real estate. However, because it's not detecting location of the tap, when I tap on the tableview, I'm not able to segue into my table view controller.
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideShowNavigation)];
tap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tap];
Ideally I would like to detect location of taps. If tapped at the top (if height <=289px), it hides navigation bar (or maybe even segue into a separate view controller where map is full screen). If tapped at the bottom (if height > 289px), then it pushes the segue into table view controller.
- (void) hideShowNavigation:(id)sender
{
[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden];
[self hidesBottomBarWhenPushed];
}
Here's the whole code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationController.navigationBar.translucent = YES;
self.automaticallyAdjustsScrollViewInsets = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(hideShowNavigation:)];
tap.numberOfTapsRequired = 1;
[self.view addGestureRecognizer:tap];
}
- (void) hideShowNavigation:(id)sender
{
CGPoint = [sender locationInView:self.view];
CGFloat y = location.y;
if(y<=289){
[self.navigationController setNavigationBarHidden:!self.navigationController.navigationBarHidden];
[self hidesBottomBarWhenPushed];
}
}

In your selector:
CGPoint location = [sender locationInView:self.view];
CGFloat x = location.x;
CGFloat y = location.y;

Related

UITabBarController Subviews Don't Appear Weirdness

I have a curious issue with an iOS 9/ObjC app I am writing using a 4 tab format. The "main" ViewController has several subviews with controls and an image. When the user taps the image a PickerView pops up and it is dismissed with another tap on the image. The other tabbed ViewControllers lead to simple views. It was all put together with IB, except for a single subview on the "main" view where I do some Quartz animation drawing superimposed on the image view there.
If I stay on and interact with just the "main" view, all works exactly as desired. The PickerView appears and disappears as designed. Tabbing to another ViewController, then back to the "main" View leads to the problem: A tap on the image view fails to pop up the Picker. The tap is recognized in the Quartz layer, fires a Notification event which is picked up in the picked up in the main view controller which in turn adds the picker subview, brings it to the front and enables interaction just as when it works properly, but the picker never appears, even though the Debugger shows it successfully added to the subview array.
Even stranger, if I tab off the main to any other tab view, then back to main, the picker now appears again as it should. This sequence is completely repeatable - tab once off the main, and the picker doesn't visualize, tab again and then back to main and the picker works.
Any thoughts on where to start looking? Thanks
Here's some code (but I'm not sure where the problem is)
#implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.picker = [[UIPickerView alloc] init];
self.picker.datasource = self;
self.picker.delegate = self;
float screenWidth = [UIScreen mainScreen].bounds.size.width;
float pickerWidth = screenWidth * 3 / 4;
// Calculate the starting x coordinate.
float xPoint = screenWidth / 2 - pickerWidth / 2;
[self.Picker setFrame: CGRectMake(xPoint, 50.0f, pickerWidth, 250.0f)];
[self.Picker setClipsToBounds:NO];
[self.Picker setBackgroundColor:[UIColor whiteColor]];
self.Picker.showsSelectionIndicator = YES;
self.Picker.userInteractionEnabled = YES;
.
.
.
UIImage *base = [UIImage imageNamed:#"Base"];
self.baseAView = [[UIImageView alloc] initWithFrame:imFrame];
[self.baseView setImage:base];
[self.view addSubview:_baseView];
self.GView = [[GView alloc] initWithFrame:imFrame];
self.GView.backgroundColor = [UIColor clearColor];
[self.view addSubview:_GView];
}
-(void) viewDidAppear:(BOOL)animated {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(doSin) name:#"touchP" object:nil];
}
- (void) doSin {
// user hits GView, draw Picker if not in View Hierarchy, if in Hierarch, grab value and resign
NSArray *subViews = [self.view subviews];
__block NSInteger foundIndex = NSNotFound;
[subViews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UIPickerView class]]) {
foundIndex = idx;
// stop the enumeration
*stop = YES;
}
}];
if (foundIndex != NSNotFound) {
// Found the UIPickerView in subviews, so grab value & remove from screen
[self.view.subviews[foundIndex] removeFromSuperview];
} else {
// not in hierarchy, so put it up on screen
[self.view addSubview:self.Picker];
[self.view bringSubviewToFront:self.Picker];
self.view.userInteractionEnabled = YES;
}
}

Remove subview when touched outside of subview

My situation is a little more complicated than the others listed.
I have a UITableView that takes up most of the screen.
Each row pops up a subview that contains more profile information. When the screen is clicked again this subview disappears. This works perfectly.
In the Navigation Bar I have a button that will display a small menu.
- (IBAction)menuButtonClicked:(UIBarButtonItem *)sender {
//If menuView exists and Menu button is clicked, remove it from view
if (self.menuView) {
self.tableView.userInteractionEnabled = true;
[self.menuView removeFromSuperview];
self.menuView = Nil;
}
//Menu View doesn't exist so create it
else {
// Create the Menu View and add it to the parent view
self.menuView = [[[NSBundle mainBundle] loadNibNamed:#"MenuView" owner:self
options:nil] objectAtIndex:0];
self.menuView.layer.cornerRadius = 20.0f;
self.menuView.layer.borderWidth = 3.0f;
self.menuView.layer.borderColor = [UIColor whiteColor].CGColor;
self.menuView.frame = CGRectMake(0, 64, self.menuView.frame.size.width,
self.menuView.frame.size.height);
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(singleTapGestureCaptured:)];
[self.menuView addGestureRecognizer:singleTap];
//Disable Selection of Profiles while Menu is showing
self.tableView.userInteractionEnabled = false;
//Add MenuView to View
[self.view addSubview: self.menuView];
}
}
//Removed Sub Views from View when tapped
-(void)singleTapGestureCaptured:(UITapGestureRecognizer *)gesture{
if(self.profileView){
[self.profileView removeFromSuperview];
self.profileView = Nil;
}
if(self.menuView) {
self.tableView.userInteractionEnabled = true;
[self.menuView removeFromSuperview];
self.menuView = Nil;
}
}
Now I want to dismiss this menus if the menu button is clicked again (working in above code) but also when the user touches out of the menu and on the tableView or navbar. If the menu is displayed, I don't want the tableView to display it's profile subview (working in above code) but just remove the menuView. I can't get the menuView to go away if I touch the tableView.
Can anyone point me in the right direction?
Make a new transparent overlay view sized to cover the entire screen. Add your menuView as a subview of the overlay, then add the overlay as a subview of your main window. Put a tap gesture recognizer on the overlay that will dismiss it when tapped.
You may need to set cancelsTouchesInView to NO on your gesture recognizer if buttons on your menu view are not working.
Roughly this (please excuse typos, I haven't compiled this):
- (void)showMenu
{
self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
overlay.backgroundColor = [UIColor clearColor];
self.menuView = /* code to load menuView */;
[overlay addSubview:self.menuView];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(onSingleTap:)];
tap.cancelsTouchesInView = NO;
[overlay addGestureRecognizer:tap];
[self.tableView.window addSubview:overlay];
}
- (void)handleSingleTap:(UITapGestureRecognizer *)sender
{
[self.overlay removeFromSuperview];
}
You might also want to add a swipe gesture recognizer to also dismiss the overlay, as someone may attempt to scroll the table expecting the menu to be dismissed.
While trying to make my own custom topdown-slide menu using my own custom NIB file, I found that this can be achieved by many techniques. I would like to suggest and share a different solution which is very similar but is created with a custom button on the background.
I've been looking around but could not find answers mentioning this.
This is very similar to the tap recogniser except for one thing - tap recogniser spreads all over the layout (including subviews), while using a layer of custom button allows you to interact with the top view and dismiss/ remove it from superview when clicking on lower layer (when lower layer is the background button). This is how I did it:
You create the layout
You add a UIButton with type UIButtonTypeCustom to the layout
You frame this layout over the view you wish to be responsive to that tap/click
You add your menu view on top of that layout and animate your menu to appear
- (void)showMenuViewWithBackgroundButtonOverlay
{
self.backgroundButton = ({
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
button.frame = self.view.frame;
[button addTarget:self action:#selector(toggleAppMenu) forControlEvents:UIControlEventTouchUpInside];
button;
});
if (!self.menu) {
self.menu = [self createMenu]; // <-- get your own custom menu UIView
}
if (!self.overlay) {
self.overlay = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.overlay.backgroundColor = [UIColor clearColor];
[self.overlay addSubview:self.backgroundButton];
[self.overlay addSubview:self.menu];
[self.view addSubview:self.overlay];
}
[self toggleAppMenu];
}
And the toggleAppMenu:
- (void)toggleAppMenu
{
CGRect nowFrame = [self.menu frame];
CGRect toBeFrame = nowFrame;
CGFloat navHeight = self.navigationController.navigationBar.frame.size.height;
CGFloat statusBarHeight = [UIApplication sharedApplication].statusBarFrame.size.height;
if (self.showingMenu) {
toBeFrame.origin.y = toBeFrame.origin.y-nowFrame.size.height-navHeight-statusBarHeight;
[UIView animateWithDuration:0.5 animations:^{
[self.menu setFrame: toBeFrame];
}completion:^(BOOL finished) {
self.showingMenu = !self.showingMenu;
[self.view endEditing:YES];
[self.overlay removeFromSuperview];
self.overlay = nil;
NSLog(#"menu is NOT showing");
}];
}
else{
toBeFrame.origin.y = navHeight+statusBarHeight;
[UIView animateWithDuration:0.5 animations:^{
[self.menu setFrame: toBeFrame];
}completion:^(BOOL finished) {
self.showingMenu = !self.showingMenu;
NSLog(#"menu is showing");
}];
}
}
I hope this will be helpful for someone.
Works on Swift 5
I create custom view and I want it hide by tap outside subview. Maybe it can help for someone or anybody can suggest a better way :)
// create tap for view
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(animateOut))
self.addGestureRecognizer(tapGesture)
// create tap for subview
let tapGesture2 = UITapGestureRecognizer(target: self, action: nil)
container.addGestureRecognizer(tapGesture2)

IBAction is not working with UITapGestureRecognizer

I am working on an application where i need to zoom a UIView on click of UITapGestureRecognizer and want to perform a IBAction while zooming and after zooming. Is this possible?. Please give me a small example of it.
- (void)viewDidLoad
{
[super viewDidLoad];
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleDoubleTap:)];
tapGestureRecognizer.numberOfTapsRequired = 2;
[self.scrollContainer addGestureRecognizer:tapGestureRecognizer];
}
- (void)handleDoubleTap:(UIGestureRecognizer *)recognizer
{
if(isAlreadyZoomed)
{
CGPoint Pointview = [recognizer locationInView:recognizer.view];
CGFloat newZoomscal = 3.0;
CGSize scrollViewSize = self.scrollContainer.bounds.size;
CGFloat width = scrollViewSize.width/newZoomscal;
CGFloat height = scrollViewSize.height /newZoomscal;
CGFloat xPos = Pointview.x-(width/2.0);
CGFloat yPos = Pointview.y-(height/2.0);
CGRect rectTozoom = CGRectMake(xPos, yPos, width, height);
[self.scrollContainer zoomToRect:rectTozoom animated:YES];
[self.scrollContainer setZoomScale:3.0 animated:YES];
isAlreadyZoomed = NO;
}
else
{
[self.scrollContainer setZoomScale:1.0 animated:YES];
isAlreadyZoomed = YES;
}
}
IBAction are just regular methods that returns void and take 0 or 1 argument only.
You can call them in code as you would call any other king of methods.
UITapGestureRecognizer are design to call an IBAction method while triggered. You can set that from InterfaceBuilder or in code
for example the following code
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(handleTap:)];
tap.numberOfTapsRequired = 2;
[imageView addGestureRecognizer:[tap cop]];
will called this methods when the user doubleTap the imageView
- (void) handleTap:(UITapGestureRecognizer *)sender;
in this methods, you can pretty much do whatever you need:
managing your subviews,
calling other methods etc...
However, if you plan an zooming in and out, I would strongly recommand the usage of the UIScrollView class instead of the UIView class.
Cheers
hi i hope below would give u a idea.....
UITapGestureRecognizer *tap2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(scrollViewDoubleTapped:)];
tap2.numberOfTapsRequired = 2;
[self.view addGestureRecognizer:tap2];
- (void) scrollViewDoubleTapped:(UITapGestureRecognizer *)sender
{
scrollZoomAdjust.zoomScale=2.0f;// set your required Zoom scale
// scrollZoomAdjust is the scroll view that contain the image within it
}
the above code was not tested

How to load new view in the same window on button click?

I've got the View-Controller with a button. I want the new view to be loaded over my View-Controller when the button is pressed. It'd not replace the existing view, I want it to be smaller than the screen and hide when I tap out of the small view.
How should it be implemented in code?
- (IBAction)button:(id)sender
{
UIView *view2=[[UIView alloc]initWithFrame:CGRectMake(0,0,200,200)];
[self.view addSubview:view2];
UITapGestureRecognizer *Tap = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(Tapview)] autorelease];
[view2 addGestureRecognizer:Tap];
}
-(void)Tapview
{
[view2 removeFromSuperview];
}
Add Tap Gesture to self.view like this:
UITapGestureRecognizer *oneFinger =
[[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(oneFingerAction:)] autorelease];
// Set required taps and number of touches
[oneFinger setNumberOfTapsRequired:1];
[oneFinger setNumberOfTouchesRequired:1];
// Add the gesture to the view
[[self view] addGestureRecognizer:oneFinger];
Add one BOOL flag in .h file; in ViewDidLoad method add this:
flag = FALSE;
Now I assume u have UIView *smallView which be added on screen like this:
[self.view addSubView:smallView];
flag = TRUE;
smallView.center = self.view.centre;
Now when tapped on self.view tap gesture action called
- (void)oneFingerAction:(UITapGestureRecognizer*)sender
{
if(sender.view == self.view)
{
if(flag){
if(smallView)
{
[smallView removeFromSuperView];
}
}
}
}

How do I center a UIImageView within a full-screen UIScrollView?

In my application, I would like to present the user with a full-screen photo viewer much like the one used in the Photos app. This is just for a single photo and as such should be quite simple. I just want the user to be able to view this one photo with the ability to zoom and pan.
I have most of it working. And, if I do not center my UIImageView, everything behaves perfectly. However, I really want the UIImageView to be centered on the screen when the image is sufficiently zoomed out. I do not want it stuck to the top-left corner of the scroll view.
Once I attempt to center this view, my vertical scrollable area appears to be greater than it should be. As such, once I zoom in a little, I am able to scroll about 100 pixels past the top of the image. What am I doing wrong?
#interface MyPhotoViewController : UIViewController <UIScrollViewDelegate>
{
UIImage* photo;
UIImageView *imageView;
}
- (id)initWithPhoto:(UIImage *)aPhoto;
#end
#implementation MyPhotoViewController
- (id)initWithPhoto:(UIImage *)aPhoto
{
if (self = [super init])
{
photo = [aPhoto retain];
// Some 3.0 SDK code here to ensure this view has a full-screen
// layout.
}
return self;
}
- (void)dealloc
{
[photo release];
[imageView release];
[super dealloc];
}
- (void)loadView
{
// Set the main view of this UIViewController to be a UIScrollView.
UIScrollView *scrollView = [[UIScrollView alloc] init];
[self setView:scrollView];
[scrollView release];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Initialize the scroll view.
CGSize photoSize = [photo size];
UIScrollView *scrollView = (UIScrollView *)[self view];
[scrollView setDelegate:self];
[scrollView setBackgroundColor:[UIColor blackColor]];
// Create the image view. We push the origin to (0, -44) to ensure
// that this view displays behind the navigation bar.
imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0, -44.0,
photoSize.width, photoSize.height)];
[imageView setImage:photo];
[scrollView addSubview:imageView];
// Configure zooming.
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
CGFloat widthRatio = screenSize.width / photoSize.width;
CGFloat heightRatio = screenSize.height / photoSize.height;
CGFloat initialZoom = (widthRatio > heightRatio) ? heightRatio : widthRatio;
[scrollView setMaximumZoomScale:3.0];
[scrollView setMinimumZoomScale:initialZoom];
[scrollView setZoomScale:initialZoom];
[scrollView setBouncesZoom:YES];
[scrollView setContentSize:CGSizeMake(photoSize.width * initialZoom,
photoSize.height * initialZoom)];
// Center the photo. Again we push the center point up by 44 pixels
// to account for the translucent navigation bar.
CGPoint scrollCenter = [scrollView center];
[imageView setCenter:CGPointMake(scrollCenter.x,
scrollCenter.y - 44.0)];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[[[self navigationController] navigationBar] setBarStyle:UIBarStyleBlackTranslucent];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:YES];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[[[self navigationController] navigationBar] setBarStyle:UIBarStyleDefault];
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault animated:YES];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return imageView;
}
#end
This code should work on most versions of iOS (and has been tested to work on 3.1 upwards).
It's based on the Apple WWDC code for PhotoScroller.
Add the below to your subclass of UIScrollView, and replace tileContainerView with the view containing your image or tiles:
- (void)layoutSubviews {
[super layoutSubviews];
// center the image as it becomes smaller than the size of the screen
CGSize boundsSize = self.bounds.size;
CGRect frameToCenter = tileContainerView.frame;
// center horizontally
if (frameToCenter.size.width < boundsSize.width)
frameToCenter.origin.x = (boundsSize.width - frameToCenter.size.width) / 2;
else
frameToCenter.origin.x = 0;
// center vertically
if (frameToCenter.size.height < boundsSize.height)
frameToCenter.origin.y = (boundsSize.height - frameToCenter.size.height) / 2;
else
frameToCenter.origin.y = 0;
tileContainerView.frame = frameToCenter;
}
Have you checked out the UIViewAutoresizing options?
(from the documentation)
UIViewAutoresizing
Specifies how a view is automatically resized.
enum {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
typedef NSUInteger UIViewAutoresizing;
Are you using IB to add the scroll view? Change the autosizing options of the scrollview to the attached image.
I think the reason behind it is because the zoomScale applies to the whole contentSize, regardless of the actual size of the subview inside the scrollView (in your case it's an imageView). The contentSize height seems to be always equal or greater than the height of the scrollView frame, but never smaller. So when applying a zoom to it, the height of the contentSize gets multiplied by the zoomScale factor as well, that's why you're getting an extra 100-something pixels of vertical scroll.
You probably want to set the bounds of the scroll view = bounds of the image view, and then center the scroll view in its containing view. If you place a view inside a scroll view at an offset from the top, you will get that empty space above it.