Attempting to "turn on/off" different UITapGestureReconginzer methods - objective-c

I have this lovely nav bar asset:
navBar view http://dl.dropbox.com/u/1734050/ViewWithNavBar.jpg
and when users tap on the middle button (one with the arrows) it will bring up this other lovely "share box" asset
shareBox dropdown from navBar http://dl.dropbox.com/u/1734050/navBarWithShareBox.jpg
What I have in the code so far is that when the screen is tapped, the nav bar will appear, and disappear on another tap. Secondly, when the user taps on the "share" button, the share box comes up, and the user can tap outside of the box to dismiss it.
Here is the problem: I cannot dismiss the nav bar after bringing up the share box!
Here is some code:
-(void)viewDidLoad {
...
...
...
UITapGestureRecognizer *tapNavBar = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapForNavBar:)];
self.tapForNavBar = tapNavBar;
[self.viewForTaps addGestureRecognizer:self.tapForNavBar];
// btw, tapForNavBar is a UITapGestureRecognizer instance variable.
// also, viewForTaps is a view (instance variable) I made to handle
// where the user could tap (most of the screen)
}
#pragma mark -
#pragma mark Tap and Gesture Methods
-(void)handleTapForNavBar:(UIGestureRecognizer *)gestureRecognizer {
if (self.navBarIsHidden) {
[UIView animateWithDuration:0.5 animations:^ {
//self.primeViewController.view.alpha = 0.8;
self.navBar.alpha = 1.0;
}];
self.detailViewButton.userInteractionEnabled = YES;
self.shareButton.userInteractionEnabled = YES;
self.aboutAppButton.userInteractionEnabled = YES;
self.navBarIsHidden = NO;
}
else {
[UIView animateWithDuration:0.5 animations:^ {
self.navBar.alpha = 0.0;
//self.primeViewController.view.alpha = 1.0;
}];
self.detailViewButton.userInteractionEnabled = NO;
self.shareButton.userInteractionEnabled = NO;
self.aboutAppButton.userInteractionEnabled = NO;
self.navBarIsHidden = YES;
}
}
Ok, so that should look all dandy (and works like it too!) -- now here is perhaps where it gets unorthodox?
-(IBAction)showShareMenu:(id)sender {
if (self.navBarShareBoxIsHidden) {
[UIView animateWithDuration:0.5 animations:^ {
self.navBarShareBox.alpha = 1.0;
}];
[self.tapForNavBar removeTarget:nil action:NULL];
UITapGestureRecognizer *tapShareBox = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapForShareBox:)];
self.tapForShareBox = tapShareBox;
[self.viewForTaps addGestureRecognizer:self.tapForShareBox];
self.navBarShareBoxIsHidden = NO;
self.twitterButton.userInteractionEnabled = YES;
self.facebookButton.userInteractionEnabled = YES;
self.googlePlusButton.userInteractionEnabled = YES;
}
else {
[UIView animateWithDuration:0.5 animations:^ {
self.navBarShareBox.alpha = 0.0;
}];
[self.tapForShareBox removeTarget:nil action:NULL];
[self.tapForNavBar addTarget:self action:#selector(handleTapForNavBar:)];
self.navBarShareBoxIsHidden = YES;
self.twitterButton.userInteractionEnabled = NO;
self.facebookButton.userInteractionEnabled = NO;
self.googlePlusButton.userInteractionEnabled = NO;
}
}
I then create this method to handle the specific shareBox tapping:
-(void)handleTapForShareBox:(UIGestureRecognizer *)gestureRecognizer {
if (!self.navBarShareBoxIsHidden) {
[UIView animateWithDuration:0.5 animations:^ {
self.navBarShareBox.alpha = 0.0;
}];
self.navBarShareBoxIsHidden = YES;
self.twitterButton.userInteractionEnabled = NO;
self.facebookButton.userInteractionEnabled = NO;
self.googlePlusButton.userInteractionEnabled = NO;
[self.tapForShareBox removeTarget:nil action:NULL];
[self.tapForNavBar addTarget:self action:#selector(handleTapForNavBar:)];
}
}
I'm assuming my problem is coming from alloc/initing the new UITapGestureRecognizer in the -(IBAction)showShareMenu method. I thought by using the removeTarget... and addTarget messages I could easily tell my code which TapGesture method it should use, but considering it isn't working, I was wrong! Where did I go wrong? If you need more info, I'd be happy to give more.

Ah Ha! Looks like I found a solution! In my handleTapForShareBox method I did this:
-(void)handleTapForShareBox:(UIGestureRecognizer *)gestureRecognizer {
if (!self.navBarShareBoxIsHidden) {
[UIView animateWithDuration:0.5 animations:^ {
self.navBarShareBox.alpha = 0.0;
}];
self.navBarShareBoxIsHidden = YES;
self.twitterButton.userInteractionEnabled = NO;
self.facebookButton.userInteractionEnabled = NO;
self.googlePlusButton.userInteractionEnabled = NO;
[self.tapForShareBox removeTarget:nil action:NULL];
//[self.tapForNavBar addTarget:self action:#selector(handleTapForNavBar:)];
// Here is the new stuff, I added another alloc init of a UITapGestureRecognizer
// and re-assigned it to my UITap instance variable
UITapGestureRecognizer *tapNavBar = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTapForNavBar:)];
self.tapForNavBar = tapNavBar;
[self.viewForTaps addGestureRecognizer:self.tapForNavBar];
}
}
It works great now! However, there were a couple things:
1) Do I have to release the UITapGestureRecognizer local pointer I created? Or because I initWithTarget it will be autoreleased?
2) Is this a hack? Or is this a proper way of passing different tap methods around? I'm just a little confused why the removeTarget and addTarget didn't do the trick.

Related

How to bring View to top of UIView Hierarchy

My app currently has a search bar and generates a holder view of results dropping down from the search bar. However, the ResultContainerView is behind my other Views. I am wondering how to bring that search bar container view to the top of the UIView hierarchy. I tried using [super bringSubviewToFront:_searchResultsHolderView];
but that did not work. Here is my method for when the searchbar is tapped.
- (void)_expandSearchResults
{
CCLogVerbose (#"");
_searchResultsHolderView.alpha = 0.0;
_searchResultsHolderView.hidden = NO;
[_searchResultsHolderView setFrameHeight:10];
[UIView beginAnimations:#"expandSearchResults" context:nil];
[UIView setAnimationDuration:kAnimationDuration_SearchResults];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector (searchAnimationDidStop:finished:context:)];
_searchResultsHolderView.alpha = 1.0;
[_searchResultsHolderView setFrameHeight:_searchResultsHt];
[super bringSubviewToFront:_searchResultsHolderView];
[UIView commitAnimations]; // start animation
}
And this is my reloadData method:
- (void)_reloadSearchResults:(NSString *)searchS
{
CCLogVerbose (#"");
if ([searchS length] == 0) {
_searchResults = nil;
[_searchResultsTableView reloadData];
return;
}
NSAssert (self.patientService != nil, #"The Patient Service is invalid");
[self.patientService searchPatients:searchS
expandType:AllPatientExpansions
success:^(NSArray *list) {
_searchResults = list;
[_searchResultsTableView reloadData];
}
failure:^(NSError *error) {
CCLogDebug (#"Failed: %#", error);
}];
}
[self.superview bringSubviewToFront:self];
[self bringSubviewToFront:_searchResultsHolderView];
First you need to bring self to the top of the stack. After that you can call bringSubViewToFront and bring your _searchResultsHolderView to the front.

Animating a subview with CentreY constraint

I have a UITableViewCell with two UILabels and a UISwitch. The labels are multilined and placed on top of each other. The UISwitch is centred vertically. When I turn off the switch I hide one of the labels and the height of the row changes. This causes the switch to jump though I call:
[self.tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
Here is my tableviewcell class implementation:
- (void)setBounds:(CGRect)bounds
{
[super setBounds:bounds];
self.contentView.frame = self.bounds;
}
- (void)setUpView{
self.numberLabel.text = [NSString stringWithFormat:#"Quote number %ld", [self.cellData[#"rowNumber"] integerValue]];
[self.controlSwitch setOn:[self.cellData[#"checked"] boolValue]];
[UIView animateWithDuration:0.5 animations:^{
if ([self.controlSwitch isOn]) {
self.quoteLabel.text = self.cellData[#"quote"];
self.quoteLabel.hidden = NO;
self.bottomConst.constant = 10;
}else{
self.quoteLabel.text = #"";
self.quoteLabel.hidden = YES;
self.bottomConst.constant = 0;
}
}];
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self.contentView updateConstraintsIfNeeded];
[self.contentView layoutIfNeeded];
self.numberLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.numberLabel.frame);
self.quoteLabel.preferredMaxLayoutWidth = CGRectGetWidth(self.quoteLabel.frame);
}
- (IBAction)removeQuote:(id)sender {
[self.delegate updateCell:self];
}
The constraint settings on the UITableViewCell looks like this.
When the switch is turned on, I'm simply unhide the Quote label and set the text.
Here is the link on how it looks like. Is this the default behavior or is there a way I can control the movement of the switch when the row animates?
Try this:
...
[self.contentView updateConstraintsIfNeeded];
[UIView beginAnimations:nil context:nil];
[self.contentView layoutIfNeeded];
[UIView commitAnimations];
...

View not receiving touches after custom spinner is invoked

I followed the guide here to make my own custom view pop up to indicate I am searching for the users results:
http://joris.kluivers.nl/blog/2012/03/02/custom-popups-revisited/
When the search results are found, I dismiss the custom view, an a Flurry Ad pops up immediately. I click the x button on the ad and my next view appears. This view is not receiving any touch events anymore, neither is the tab bar at the bottom working. I suspect it has to do something with the custom view still animating away(via a transform to 0.0f) when the Flurry ad is coming up, but I am not sure. I am including the code for how the spinner is created, as well as the dismissal.
Note: I have confirmed the problem is related to Flurry Ads showing up modally while my customSpinner is going away. I don't know how to fix it though. When I don't display the Ad, the touch events are registering fine.
PCFFinder.c
- (void)queryServer:(NSString *)queryString {
__block PCFCustomSpinner *spinner = [[PCFCustomSpinner alloc] initWithFrame:CGRectMake(0, 0, 200, 200):#"Searching.."];
[spinner show];
NSString *url = #"https://selfservice.mypurdue.purdue.edu/prod/bwckschd.p_get_crse_unsec";
NSString *referer = #"https://selfservice.mypurdue.purdue.edu/prod/bwckgens.p_proc_term_date";
dispatch_queue_t task = dispatch_queue_create("Task 3", nil);
dispatch_async(task, ^{
NSString *webData = [PCFWebModel queryServer:url connectionType:#"POST" referer:referer arguements:queryString];
if (webData) {
classesOffered = [PCFWebModel parseData:webData type:2];
dispatch_async(dispatch_get_main_queue(), ^{
[spinner dismiss];
spinner = NULL;
if ([classesOffered count] > 0) {
if (self.view.window)
searching = NO;
//the ad pops up in the next view controllers viewWillAppear method and after exiting the ad, the views no longer work
[self performSegueWithIdentifier:#"ChooseClass" sender:self];
}else {
[spinner dismiss];
PCFCustomAlertView *alert = [[PCFCustomAlertView alloc] initAlertView:CGRectMake(0, 0, 300, 200) :#"Search Results" :#"No results were found. Please try broadening the search or double check your input." :#"OK"];
[alert show];
//the views still work here though..
searching = NO;
}
});
//rest omitted because it is irrelevant.
}
CustomPopup.c
-(void)dismiss
{
__block UIWindow *animatedWindow = self.window;
[UIView animateWithDuration:0.3f delay:0.0f options:UIViewAnimationCurveEaseOut animations:^{
animatedWindow.transform = CGAffineTransformMakeScale(0.0f, 0.0f);
}completion:^(BOOL finished) {
animatedWindow.hidden = YES;
animatedWindow = nil;
}];
}
-(void)show
{
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.windowLevel = UIWindowLevelAlert;
self.window.backgroundColor = [UIColor clearColor];
self.center = CGPointMake(CGRectGetMidX(self.window.bounds), CGRectGetMidY(self.window.bounds));
[self.window addSubview:self];
[self.window makeKeyAndVisible];
//animated
self.window.transform = CGAffineTransformMakeScale(1.5f, 1.5f);
self.window.alpha = 0.0f;
__block UIWindow *animationWindow = self.window;
[UIView animateWithDuration:0.3f delay:0 options:UIViewAnimationCurveEaseIn animations:^{
animationWindow.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
animationWindow.alpha = 1.0f;
}completion:nil];
}
I have tried setting the view as first responder in the viewWillAppear method, but that doesn't work either.
In your dismiss method you are setting the single window to have a zero scale and hidden. This is surely your problem. Animate the pop ups view then remove it from the super view instead.

How to flip an individual UIView (without flipping the parent view)

This is an iPad project where I have a UIView with several subViews, and I am trying to animate one of this UIViews using [UIView transitionFromView:toView:duration:options:completion], but when I run this method the whole parent view gets flipped!
This is the code I'm using:
UIView *secondView = [[UIView alloc] initWithFrame:CGRectMake(200, 200, 300, 300)];
[newView setBackgroundColor:[UIColor redColor]];
[UIView transitionFromView:self.firstView.view toView:secondView duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:nil];
Any idea on how can I flip between this views without animating the whole parent view?
Thanks!
this code may helps you:
put the two views you want to flip inside an unnamed view with the same size
and link the IBOutlet UIView *newView,*oldView; to the views and put the new view on top
bool a = NO;
#implementation ViewController
- (IBAction)flip:(id)sender
{
if (a == NO) {
[UIView transitionFromView:oldView toView:newView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
a = YES; // a = !a;
}
else {
[UIView transitionFromView:newView toView:oldView
duration:1.0
options:UIViewAnimationOptionTransitionFlipFromLeft
completion:NULL];
a = NO; // a = !a;
}
}
I had problems getting this to work also. I used the code written by Floris, but although it worked for the first "flip" the next flip started to go weird where the buttons and labels on the frontside UI View lost there positions.
I put the code below into place and it is working fine.
Couple of things to note:
panelView is a UIView control on the ViewController that has two other UIViews nested inside it (subviews). The first one is called frontView, second one is called backView. (see picture below)
I have a bool property called displayingFront on the class
(in my .h)
#property BOOL displayingFront;
in my .m
#synthesize displayingFront;
in my viewDidLoad method
self.displayingFront = YES;
This is the code in the .m that i have rigged up to the two buttons if front and back UIViews...
- (IBAction)flip:(id)sender
{
[UIView transitionWithView:self.panelView
duration:1.0
options:(displayingFront ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft)
animations: ^{
if(displayingFront)
{
self.frontView.hidden = true;
self.backView.hidden = false;
}
else
{
self.frontView.hidden = false;
self.backView.hidden = true;
}
}
completion:^(BOOL finished) {
if (finished) {
displayingFront = !displayingFront;
}
}];
}
Use a container view to hold your subviews, and make the container view the same size as your subview that you're trying to animate.
So, you can setup your views like this.
self.view-> "a container view" -> subviews
**//flipping view continuously**
bool a = NO;
-(void)viewDidLoad
{
// THIS is the canvass holding the two views this view is flipping
UIView *v=[[UIView alloc]initWithFrame:CGRectMake(40, 40, 150, 150)];
v.backgroundColor=[UIColor clearColor];
[self.view addSubview:v];
[v addSubview:yourfirstview];
[v addSubview:yoursecondview];
// calling the method
[NSTimer scheduledTimerWithTimeInterval:2.0
target:self
selector:#selector(flip)
userInfo:nil
repeats:YES];
}
//flipping view randomly
-(void)flip
{
if (a == NO)
{
[UIView transitionFromView:yourfirstview toView:yoursecondview duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
a = YES;
}
else if(a==YES)
{
[UIView transitionFromView:yoursecondview toView:yourfirstview duration:1.0 options:UIViewAnimationOptionTransitionFlipFromLeft completion:NULL];
a = NO;
}
}
I fixed this problem by using old-school animations:
[UIView beginAnimations:#"Flip" context:nil];
[UIView setAnimationDuration:1];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromRight forView:firstView cache:YES];
[self addSubview:secondView];
[UIView commitAnimations];
Additionally you can remove the firstView:
[firstView removeFromSuperview];
I'd like to make an update of Steve Parker's answer in SWIFT 4:-
var displayBlankView:Bool = true
UIView.transition(with:flipView, duration:1.0, options:displayBlankView ? .transitionFlipFromLeft:.transitionFlipFromRight, animations:{
if(self.displayBlankView){
self.view1.isHidden=true
self.view2.isHidden=false
}else{
self.view1.isHidden=false
self.view2.isHidden=true
}
}, completion:{
(finished: Bool) in
self.displayBlankView = !self.displayBlankView;
})

Why is my iAd banner not showing?

- (void)viewDidLoad
{
adView = [[ADBannerView alloc] initWithFrame:CGRectZero];
adView.requiredContentSizeIdentifiers = [NSSet setWithObject:ADBannerContentSizeIdentifierPortrait];
adView.currentContentSizeIdentifier = ADBannerContentSizeIdentifierPortrait;
CGRect adFrame = adView.frame;
adFrame.origin.y = self.view.frame.size.height-adView.frame.size.height;
adView.frame = adFrame;
[self.view addSubview:adView];
}
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
if (!self.bannerIsVisible)
{
self.bannerIsVisible = YES;
}
}
- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
if (self.bannerIsVisible)
{
self.bannerIsVisible = NO;
}
There are a four things. First, you should be positioning the banner off screen in your viewDidLoad method because it will just show an empty frame when you first launch and will more than likely get rejected because of it.
Secondly, you are setting up your banner view incorrectly. I think the frame is still CGZero. Thirdly you are not setting the bannerView's delegate. Try the following:
-(void)viewDidLoad{
CGRect frame=CGRectZero;
frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier:ADBannerContentSizeIdentifierPortrait];
// Place frame at the bottom edge of the screen out of sight
frame.origin = CGPointMake(0.0, CGRectGetMaxY(self.view.bounds));
// Now to create and configure the banner view
ADBannerView *adView = [[ADBannerView alloc] initWithFrame:frame];
adView.requiredContentSizeIdentifiers = [NSSet setWithObject:ADBannerContentSizeIdentifierPortrait];
// Set the delegate to self, so that we are notified of ad responses
adView.delegate = self;
[self.view addSubview: adView];
}
Fourth, in your bannerViewDidLoadAd: method you are not animating the banner ad into place. Try this:
- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
// Get a brand new frame
CGRect newFrame=CGRectZero;
CGPoint frameOrigin=CGPointZero;
// Set the origin
frameOrigin=CGPointMake(0.0, CGRectGetMaxY(self.view.bounds));
newFrame.origin=frameOrigin;
// Set the size
newFrame.size=[ADBannerView sizeFromBannerContentSizeIdentifier:ADBannerContentSizeIdentifierPortrait];
CGFloat bannerHeight = newFrame.size.height;
CGFloat bannerOffset=0.0;
// Determine where the new frame should be
if (!self.bannerIsVisible)
{
// It should be visible, raise it up
bannerOffset=-bannerHeight;
}
CGRect offSetRect=CGRectOffset(newFrame,0.0f,bannerOffset);
[UIView animateWithDuration:0.2
animations:^{banner.view.frame=offSetRect}
completion:^(BOOL finished){
if (bannerOffSet<0){
self.bannerIsVisible=YES;
}else{
self.bannerIsVisible=NO;
}
}
];
}
of course if the banner is supposed to be positioned at the top of the screen, you can probably figure out how things need to be modified, but this gets you in going in the right direction.
Good luck
Not sure by reading your question, but do be aware that Apple simulates iAds not being available. Sometimes you need to try multiple times before the sample ad comes through.