I have the following code in a view controller:
#interface QuizController ()
#property (weak, nonatomic) IBOutlet UITextView *questionText;
#property (weak, nonatomic) IBOutlet UITextView *one;
#property (weak, nonatomic) IBOutlet UITextView *two;
#property (weak, nonatomic) IBOutlet UITextView *three;
#property (weak, nonatomic) IBOutlet UITextView *four;
#property(strong, nonatomic) NSMutableArray* possAnswers;
#end
#implementation QuizController
- (void)viewDidLoad {
[super viewDidLoad];
[self.manager setupGame];
self.possAnswers = [[NSMutableArray alloc] initWithObjects:#"a", #"b", #"c", #"d", nil];
}
//This listens for a tap on any of the TextViews and sets the background to Blue
- (IBAction)fourTapped:(id)sender {
NSLog(#"There's been a tap!");
[self blankBoxes];
if( [sender isKindOfClass:[UITapGestureRecognizer class]] )
{
UITapGestureRecognizer* tgr = (UITapGestureRecognizer*)sender;
UIView* view = tgr.view;
if( [view isKindOfClass:[UITextView class]]){
UITextView* tv = (UITextView*)view;
NSLog([tv text]);
[tv setBackgroundColor:[UIColor blueColor]];
}
}
}
-(void)blankBoxes
{
[_one setBackgroundColor:[UIColor whiteColor]];
[_two setBackgroundColor:[UIColor whiteColor]];
[_three setBackgroundColor:[UIColor whiteColor]];
[_four setBackgroundColor:[UIColor whiteColor]];
}
However, the only UITextView that gets highlighted is _one, no matter which view is tapped. I don't understand why this is. I'd be really grateful if anyone could point this out to me.
Gesture recognizers attach to one and only one view. That's by design. If you want a single gesture recognizer that lets you recognize taps on multiple views, you have to attach it to a common, enclosing superview, then look at the tap coordinates and figure out which view was tapped.
Normally you just create a different gesture recognizer for each view that needs to responds to taps.
Add the UIGestureRecognizer to a UIView that contains your subviews. After that use CGRectContainsPoint() to check if the tap location is in one of the subviews, then continue from there.
CGPoint *touchLocation = [gesture locationInView:self];
for(UIView *view in self.subviews){
if(CGRectContainsPoint(view.frame, touchLocation))
//Your code here
}
Related
I'm starting to learn iOS gesture recognizers, going through this tutorial http://www.appcoda.com/ios-gesture-recognizers/. Have a problem translating a view with UIPanGestureRecognizer while setting the UILabels' text to the gesture's velocity.
I have been successfully translating a view with a pan gesture like this (with the help of IB):
PanViewController.h
#interface PanViewController : UIViewController
#property (weak, nonatomic) IBOutlet UIView *testView;
#property (weak, nonatomic) IBOutlet UILabel *horizontalVelocityLabel;
#property (weak, nonatomic) IBOutlet UILabel *verticalVelocityLabel;
#end
PanViewController.m
#interface PanViewController ()
- (void)moveViewWithGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer;
#end
#implementation PanViewController
- (void)viewDidLoad {
[super viewDidLoad];
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(moveViewWithGestureRecognizer:)];
[self.testView addGestureRecognizer:panGestureRecognizer];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)moveViewWithGestureRecognizer:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint touchLocation = [panGestureRecognizer locationInView:self.view];
self.testView.center = touchLocation;
}
But when I add the following lines to this method, the view stops translating, but the UILabels get updated with the velocity:
CGPoint velocity = [panGestureRecognizer velocityInView:self.view];
self.horizontalVelocityLabel.text = [NSString stringWithFormat:#"Horizontal Velocity: %.2f points/sec", velocity.x];
self.verticalVelocityLabel.text = [NSString stringWithFormat:#"Vertical Velocity: %.2f points/sec", velocity.y];
I'd like to understand what i'm doing wrong. How can I fix it?
Thanks in advance.
I'm trying to hide a UIView and objects within it when a button is deselected. The button is located in a TableView section header contained in the same ViewController.
The button press is being stored in...
Activity.h
#property BOOL invitesAll;
Activity.m
#import "Activity.h"
#dynamic invitesAll;
And the rest of the code...
InviteContactsViewController.h
#property (weak, nonatomic) IBOutlet NSLayoutConstraint *expirationViewHeightConstraint;
#property (weak, nonatomic) IBOutlet UILabel *timeToRespondLabel;
#property (weak, nonatomic) IBOutlet UISlider *expirationSlider;
#property (weak, nonatomic) IBOutlet UILabel *expirationLabel;
#property (strong, nonatomic) IBOutlet UIButton *inviteAll;
InviteesTableViewController.h
#import "InviteContactsViewController.h"
#property (weak, nonatomic) NSLayoutConstraint *expirationViewHeightConstraint;
#property (weak, nonatomic) UILabel *timeToRespondLabel;
#property (weak, nonatomic) UISlider *expirationSlider;
#property (weak, nonatomic) UILabel *expirationLabel;
InviteesTableViewController.m
#import "InviteesTableViewController.h"
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
// create button
InviteAllButton *inviteAllButton = [InviteAllButton buttonWithType:UIButtonTypeCustom];
[inviteAllButton setTitle:#"Order Matters" forState:UIControlStateSelected];
[inviteAllButton setTitle:#"Order Doesn't Matter" forState:UIControlStateNormal];
inviteAllButton.titleLabel.font = [UIFont fontWithName:#"Avenir" size:11.0];
inviteAllButton.titleLabel.numberOfLines = 2;
[inviteAllButton addTarget:self action:#selector(inviteAllAction:) forControlEvents:UIControlEventTouchUpInside];
[headerView addSubview:inviteAllButton];
// set inviteAll button state
inviteAllButton.selected = !_activity.invitesAll;
// set constraints
[inviteAllButton setTranslatesAutoresizingMaskIntoConstraints:NO];
NSDictionary *views = NSDictionaryOfVariableBindings(inviteAllButton, titleLabel);
[headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"[titleLabel]-(>=5)-[inviteAllButton(96)]-5-|" options:0 metrics:nil views:views]];
[headerView addConstraint:[NSLayoutConstraint constraintWithItem:inviteAllButton attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:headerView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0.0]];
}
- (void)inviteAllAction:(UIButton *)sender {
sender.selected = !sender.selected;
_activity.invitesAll = !_activity.invitesAll;
[self validateReordering];
[self.tableView reloadData];
NSString *state = _activity.invitesAll ? #"invitesAll" : #"orderMatters";
// In my attempt to hide the view, I've set an expirationViewHeightConstraint
// and try to change its constant as the button is pressed
if (self.activity.invitesAll) {
self.expirationViewHeightConstraint.constant = 0.f;
self.timeToRespondLabel.hidden = YES;
self.expirationSlider.hidden = YES;
self.expirationLabel.hidden = YES;
} else {
self.expirationViewHeightConstraint.constant = 35;
self.timeToRespondLabel.hidden = NO;
self.expirationSlider.hidden = NO;
self.expirationLabel.hidden = NO;
}
}
I referenced Max's answer on changing the height constraint constant to 0.f and tried to follow Simon's answer on accessing properties across ViewControllers as a basis, but to no avail. When I run this nothing visible happens with the expirationViewHeightConstraint, timeToRespondLabel, expirationSlider, or expirationLabel IBOutlets when inviteAll is pressed.
I'm a newb so guessing I'm missing something basic here. Is my approach flawed?
What you're doing with the property declarations in InviteesTableViewController is wrong. You can't just declare some properties in the child controller and expect that they will point to objects in some other controller (parent or not). You need to get a reference to the parent, which you can using self.parentViewController, and then access its properties with that,
InviteContactsViewController *inviteVC = (InviteContactsViewController *)self.parentViewController;
if (self.activity.invitesAll) {
inviteVC.expirationViewHeightConstraint.constant = 0.f;
inviteVC.timeToRespondLabel.hidden = YES;
inviteVC.expirationSlider.hidden = YES;
inviteVC.expirationLabel.hidden = YES;
} else {
inviteVC.expirationViewHeightConstraint.constant = 35;
inviteVC.timeToRespondLabel.hidden = NO;
inviteVC.expirationSlider.hidden = NO;
inviteVC.expirationLabel.hidden = NO;
}
You should delete all those property declarations in InviteesTableViewController.
I'm am trying to delete an image while it is moving on the screen using UITapGestureRecognizers and UIViewAnimation. I have used the UIViewAnimationOptionAllowUserInteraction, but I still get the SIGBART error. I have been looking through the internet and couldn't find a solution.
This is my .m file
#import "AbcViewController.h"
#interface AbcViewController ()
#end
#implementation AbcViewController
//single tap gesture action /deletes person
-(void) tapped:(UITapGestureRecognizer *)recognizer{
[recognizer.view removeFromSuperview];
}
//double tap gesture action
-(void) tapped2:(UITapGestureRecognizer *)recognizer{
[recognizer.view removeFromSuperview];
}
- (void)viewDidLoad
{
[super viewDidLoad];
//this makes a zombie/person
UIImageView *zombieView =[[UIImageView alloc] initWithFrame:CGRectMake(135 , 300, 50, 75)];
UIImage *zombie=[UIImage imageNamed:#"free-vector-stick-figure-clip-art_105575_Stick_Figure_clip_art_hight.png"];
[zombieView setImage:zombie];
[self.view addSubview:zombieView];
[self.view insertSubview:zombieView belowSubview:_Railing];
[zombieView setUserInteractionEnabled:TRUE];
// double tap gesture recognizer
UITapGestureRecognizer *touch2 = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped2:)];
[touch2 setDelegate: self];
[touch2 setNumberOfTapsRequired: 2];
[zombieView addGestureRecognizer:touch2];
//tap gesture recognizer
UITapGestureRecognizer *touch = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapped:)];
[touch setDelegate:self];
[touch setNumberOfTapsRequired: 1];
[zombieView addGestureRecognizer:touch];
//This makes the Person move down until he is behind the railing
[UIView animateWithDuration:8.5
delay:0.0
options:UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionCurveLinear
animations:^{
CGAffineTransform transform= CGAffineTransformMakeTranslation(0,-200);
zombieView.transform = transform;
}
completion:nil];
};
-(void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
This is my .h file:
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface AbcViewController : UIViewController <UIGestureRecognizerDelegate>
#property (strong, nonatomic) IBOutlet UIImageView *Railing;
//#property (strong, nonatomic) IBOutlet UIImageView *Person;
#property (strong, nonatomic) IBOutlet UIImageView *zombieView;
#property (strong, nonatomic) IBOutlet UIImage *zombie;
-(void) tapped:(UITapGestureRecognizer *)recognizer;
-(void) tapped2:(UITapGestureRecognizer *)recognizer;
#end
Every time I click it in mid-animation I get an error but when it's complete it works fine. I am fairly new to this so I'm sure it's simple, it always is. But, thank you for your patience and answers!
The object during the animation is not where you see it. It has already moved to its final position. Animation is an illusion: the "presentation layer" is animated, but the underlying "model layer" (the real object) stands still and has already moved when the animation starts. The model layer is the one inside a view so it is the only one that can be tapped.
Tapping an object while it is being animated is therefore a very tricky problem. I discuss it here:
http://www.apeth.com/iOSBook/ch18.html#_hit_testing_during_animation
Perhaps that discussion will give you some ideas. But no matter what, this is not going to be easy.
(Consider that perhaps you should not be using view animation for this kind of game. Sprite Kit was invented for just this sort of thing.)
I have this code to embed font to all text field on all UIView :
for (UIView *subview in [[self view] subviews]) {
if ([subview isKindOfClass:[UITextField class]]) {
UITextField *textField = (UITextField *)subview;
[textField setFont:[UIFont fontWithName:#"ABeeZee-Regular" size:14]];
}
}
but, it's just embedding font to text field and it will do to all UIView. on the other hand, I want to create some method to specific UIView, so I decided to add each UIView on my interface file :
#property (strong, nonatomic) IBOutlet UIView *viewOne;
#property (strong, nonatomic) IBOutlet UIView *viewTwo;
#property (strong, nonatomic) IBOutlet UIView *viewThree;
now, how to create for loop to 'select' all UI components (button, label, textfield, etc) on (let's say) UIView viewOne, so that I can create method to it? for example, changing all component alpha = 0?
thank you.
How about this:
for (UIView *subview in self.viewOne.subviews) {
if ([subview respondsToSelector:#selector(setAlpha:)])
subview.alpha = 0;
}
Simple code is here:
Custom MyButton.h
#interface PaiLifeReadingListButton : UIButton
#property (strong, nonatomic) UIImageView *logoImageView;
#end
Custom MyButton.m
#implementation PaiLifeReadingListButton
#synthesize logoImageView = _logoImageView;
-(id)init
{
_logoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(33.0, 20.0, 80.0, 80.0)];
[_logoImageView setImage:[UIImage imageNamed:#"pic"]];
[self addSubview: _logoImageView];
}
#end
Custom MyView.h:
#property (strong, nonatomic) Mybutton *mybutton;
Custom MyView.m:
-(id) init
{
myButton = [[MyButton alloc] init];
[self addSubview:myButton];
}
ViewController.m:
#synthesize myView;
- (void)viewDidLoad
{
myView = [[Myview alloc] init];
[myView.myButton addTarget:self action:#selector(pressed:) forControlEvents:UIControlEventTouchDown];
self.view = myView;
}
-(void)pressed : (MyButton *)button
{
UIImageView *newImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"other-pic"] highlightedImage:nil];
myView.myButton.logImageView = newImageView;
[self.view setNeedsDisplay]; //----this does not work
}
When I press the button, the imageview does not change. How can I fix this?
Thank you very much!
First thing , you don't need setNeedsDisplay here. setNeedsDisplay is used for redrawing the view (it internally calls the drawRect: method of the view if required) which is not what you want.
Second, you have given the class of your button as MyButton,but in the actual .h and .m files, it is given as #interface PaiLifeReadingListButton and #implementation PaiLifeReadingListButton respectively.
Another thing, the view and button is not initialized with a frame. But I assume you have done that already, since your only problem is the image view not changing.
In your code in pressed method you should change views in the hierarchy, but you change only the object member. You can do something like
myView.myButton.logImageView.image = [UIImage imageNamed:#"other-pic"];