I have a list of data coming from a database and one of the columns is a date field. Inside that date field I have the date and time. To save room, I have a tap feature to switch between the date and time each time the user touches that uilabel. My issue is trying to know which uilabel the user touched. I have each uilabel with it's own tag incrementing by 1, but I'm confused on how to tell my function which label got pressed.
This is inside a for loop:
_short_time = [date substringFromIndex:range.location + 2];
_short_date = [date substringWithRange:NSMakeRange(0, range.location)];
_dateSwitch = [[UILabel alloc] initWithFrame:CGRectMake(7, 9, 75, 20)];
_dateSwitch.textColor = [UIColor colorWithRed:1 green:1 blue:1 alpha:1];
_dateSwitch.text = _short_date;
_dateSwitch.tag = i + 1;
_dateSwitch.textAlignment = NSTextAlignmentCenter;
[_dateSwitch setFont:[UIFont systemFontOfSize:13]];
UITapGestureRecognizer *gSwap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(switchDateTime:)];
This is the function being called "switchDateTime":
- (void) switchDateTime: (UITapGestureRecognizer *) sender
{
if (state == 1) {
if (_dateSwitch.tag == _fId) {
[_dateSwitch setText:_short_date];
state = FALSE;
}
} else {
[_dateSwitch setText:_short_time];
state = TRUE;
}
}
You have to add your gesture recognizer on your label like this:
[_dateSwitch addGestureRecognizer:gSwap];
Then on your method to identify which view the gesture recognizer is attached to:
UILabel *tappedLabel = (UILabel *)sender.view;
Related
I am developing an IOS App. I am creating a text field and a button dynamically, and I want to get textfield's text on button click, but I'm getting an error:
[UIView setText:]: unrecognized selector sent to instance
Here is my code:
- (IBAction)customFieldAdd:(id)sender{
[_addfieldArray addObject:_field];
x = 10;
y = 10;
for( int i = 0; i < [_addfieldArray count]; i++ ) {
UITextField *copyfield = [[UITextField alloc]initWithFrame:CGRectMake(5.0, x + _field.frame.size.height, 195.0f, 30.0f)];
[copyfield setDelegate:self];
[copyfield setTag:i];
[_filterPossibleValueView addSubview:copyfield];
x = x+_field.frame.size.height+10;
UIButton *copyAddButton = [[UIButton alloc]initWithFrame:CGRectMake(202.0f, y + _addField.frame.size.height, 30.0f, 30.0f)];
[copyAddButton setTag:i];
[copyAddButton addTarget:self action:#selector(customFieldDelete:) forControlEvents:UIControlEventTouchUpInside];
[_filterPossibleValueView addSubview:copyAddButton];
y = y+_addField.frame.size.height+10;
count++;
}
}
- (IBAction)customFieldDelete:(id)sender{
UIButton *button = (UIButton*)sender;
NSInteger index = button.tag;
// [_addfieldArray removeObjectAtIndex:index];
UITextField *field = [_filterPossibleValueView viewWithTag:index];
NSString *myText = field.text;
}
The default value for the tag property is 0, and you are setting that value to one of the text fields. So, when you try to get the view with tag == 0, it will have more than one view with that tag.
You should use an offset when setting the tag:
[copyfield setTag:kOffset + i];
...
[copyAddButton setTag:kOffset*2 + i]; // note that the button and the text field should not have the same tag either
where kOffset is defined (for example) like this:
let kOffset: Int = 1000
Then, to get the text field's text, you can use:
NSInteger tag = button.tag - kOffset;
UITextField *field = [_filterPossibleValueView viewWithTag:tag];
NSString *myText = field.text;
I am using the following code to attach gesture recognizers to a custom view. But for some reason I am not able to get the gestures to work.
for(NSString *option in _options) {
UIView *blind = [self createBlind:option];
blind.userInteractionEnabled = YES;
blind.tag = index;
[self addSubview:blind];
[self addGravityBehaviorToBlind:blind];
[self addCollisionBehaviorToBlind:blind];
[self addElasticityBehaviorToBlind:blind];
[self registerGestureRecognizer:blind];
_spacingY += 44;
index++;
}
}
-(void) registerGestureRecognizer:(UIView *) blind {
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(blindTapped:)];
[blind addGestureRecognizer:tapGestureRecognizer];
}
-(void) blindTapped:(UITapGestureRecognizer *) recognizer { <-- THIS NEVER GETS CALLED
NSLog(#"blindTapped");
}
For some reason the blindTapped method never gets called even if I tap on the blind view.
UPDATE:
Yes, I see the views on the screen. The views (blind) is a subview in another view. The parent view gets added to the controller.view. There is no place where I have disabled interaction.
Here is the createBlind method:
-(UIView *) createBlind:(NSString *) option {
UIView *blind = [[UIView alloc] initWithFrame:CGRectMake(0, _spacingY, 200, 44)];
blind.layer.borderWidth = 1.0f;
blind.layer.borderColor = [UIColor whiteColor].CGColor;
UILabel *optionTitleLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, blind.bounds.size.width, blind.bounds.size.height)];
optionTitleLabel.text = option;
optionTitleLabel.font = [UIFont fontWithName:#"GillSans" size:17];
optionTitleLabel.textColor = [UIColor whiteColor];
optionTitleLabel.textAlignment = NSTextAlignmentCenter;
[blind addSubview:optionTitleLabel];
return blind;
}
I have removed all the gravity and UIKit methods but still I am not able to trigger the tap gesture.
SOLUTION:
When adding the custom view to my controller.view I was not setting the frame. Once the frame was setup everything worked.
I have a NSAttributedString like so:
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString:#"testing it out #clickhere"];
NSInteger length = str.length;
[str addAttribute:NSForegroundColorAttributeName value:[UIColor bestTextColor] range:NSMakeRange(0,length)];
The NSMutableAttributedString gets set to a UILabel like so:
label.attributedText = str;
How do I make a tap gesture (or something clickable) to another viewcontroller for the words '#clickhere in the string above?
Thanks!
I think, the best way is adding the UIGestureRecognizer to your UILabel and validate the frame that you would like.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[_yourLabel addGestureRecognizer:singleTap];
- (void)handleTap:(UITapGestureRecognizer *)tapRecognizer
{
CGPoint touchPoint = [tapRecognizer locationInView: _yourLabel];
//Modify the validFrame that you would like to enable the touch
//or get the frame from _yourLabel using the NSMutableAttributedString, if possible
CGRect validFrame = CGRectMake(0, 0, 300, 44);
if(YES == CGRectContainsPoint(validFrame, touchPoint)
{
//Handle here.
}
}
Simply first add a gesture to your label
[label setUserInteractionEnabled:YES];
UITapGestureRecognizer *gesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
[label addGestureRecognizer:gesture];
control your gesture area in the below method
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
static CGRect touchableRect = CGRectMake(100.0f, 0.0f, 100.0f, 50.0f); // Give your rect as you need.
CGPoint touchPoint = [gestureRecognizer locationInView:self.view];
if (CGRectContainsPoint(touchableRect, touchPoint))
{
//User has tap on where you want. Do your other stuff here
}
}
UITapGestureRecognizer *Tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(tapDetected)];
Tap.numberOfTapsRequired = 1;
// label Name Is your Label Name
[labelName addGestureRecognizer:Tap];
-(void)tapDetected
{
//your code
}
I'd just like to add on Ramshad's answer, about how to deal with the valid frame.
For this you might want to consider using UITextView instead of UILabel, which doesn't give you access to how it manages layout the text. By disabling editing, selection and scrolling, UITextView behaves roughly the same as a UILabel, except some padding you have to remove.
For convenience, you might want to add a little category to UITextView, in which you write a method to test if a point touches any of the characters in range.
- (BOOL)point:(CGPoint)point touchesSomeCharacterInRange:(NSRange)range
{
NSRange glyphRange = [self.layoutManager glyphRangeForCharacterRange:range actualCharacterRange:NULL];
BOOL touches = NO;
for (NSUInteger index = glyphRange.location; index < glyphRange.location + glyphRange.length; index++) {
CGRect rectForGlyphInContainer = [self.layoutManager boundingRectForGlyphRange:NSMakeRange(index, 1) inTextContainer:self.textContainer];
CGRect rectForGlyphInTextView = CGRectOffset(rectForGlyphInContainer, self.textContainerInset.left, self.textContainerInset.top);
if (CGRectContainsPoint(rectForGlyphInTextView, point)) {
touches = YES;
break;
}
}
return touches;
}
This would also work for a fragment of text containing multiple words spreading across multiple lines due to word wrap. It would also work on localized texts as we deal with glyphs that are printed.
The idea is the same as the accepted answer. Here is the way in Swift.
Suppose you have set up your label ready.
youLabel.text = "please tab me!"
Add tapGestuerRecognizer to your label
let tap = UITapGestureRecognizer(target: self, action: #selector(tapAction))
yourLabel.addGestureRecognizer(tap)
Add the String extension method to String to calculate the string rect.
extension String {
func rect(font: UIFont) -> CGRect {
let label = UILabel(frame: .zero)
label.font = font
label.text = self
label.sizeToFit()
return label.frame
}
}
Calculate the available tap rect of the tap gesture.
let pleaseStringRect = "please".rect(font: yourFont)
let tapStringRect = "tap".rect(font: yourFont)
let tapAreaRect = CGRect(x: pleaseStringRect.width, y: tapStringRect.origin.y, width: tapStringRect.width, height: tapStringRect.height"
In the action do what you want
#objc private func tapAction(tap: UITapGestureRecognizer) {
let position = tap.location(in: content)
guard reporterFirstNameRect.contains(position) else {
return
}
// Do the tap action stuff
}
That's it, happy coding.
I have two buttons for adding and deleting the textfields. Adding works fine but when I click on delete it deletes only the last added textfield. Here are my two methods:
-(void)addTextField {
keyTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, yAxisDistance, 150, 30)];
keyTextField.borderStyle = UITextBorderStyleRoundedRect;
keyTextField.placeholder = #"Key Value";
keyTextField.delegate = self;
[self.view addSubview:keyTextField];
valueTextField = [[UITextField alloc] initWithFrame:CGRectMake(165, yAxisDistance, 150, 30)];
valueTextField.borderStyle = UITextBorderStyleRoundedRect;
valueTextField.placeholder = #"Value";
valueTextField.delegate = self;
[self.view addSubview:valueTextField];
yAxisDistance = yAxisDistance+35;
}
-(void)deleteTextField {
[keyTextField removeFromSuperview];
[valueTextField removeFromSuperview];
yAxisDistance = yAxisDistance-35;
}
I know it's an small issue but I am very new to this field so kindly help.
Use this code for remove specific textfield from UIView. But First you have to set tag of every UITextField in view when you create or add it in view.
for ( UITextField* textField in view.subviews )
{
if(textField.tag== 1)
{
[textField removeFromSuperview];
}
}
I think the problem lies with the outlet. Can you check the following:
Open the outlets that are connected to the textifeld in the IB. There should be gray dots inside circles on the left side of the editor. Are they seem correct for both text fields?
Setup a breakpoint inside deleteTextField method and check the two textfields. Verify that both properties are not nil.
PS: You don't need to add tags to your view, using properties is perfectly fine and even better in my opinion. The reason for your problem is something else. Also, you do not need to removeFromSuperview, you can also setHidden:YES.
Sorry that time I was not understand your problem...
I done with this:
Declare one
NSMutableArray *allTextfieldArray;
and initialise in
viewdidload
method..
now do:
-(void)addTextField {
keyTextField = [[UITextField alloc] initWithFrame:CGRectMake(10, yAxisDistance, 150, 30)];
keyTextField.borderStyle = UITextBorderStyleRoundedRect;
keyTextField.placeholder = #"Key Value";
keyTextField.delegate = self;
[self.view addSubview:keyTextField];
valueTextField = [[UITextField alloc] initWithFrame:CGRectMake(165, yAxisDistance, 150, 30)];
valueTextField.borderStyle = UITextBorderStyleRoundedRect;
valueTextField.placeholder = #"Value";
valueTextField.delegate = self;
[self.view addSubview:valueTextField];
yAxisDistance = yAxisDistance+35;
[allTextfieldArray addObject:keyTextField];
[allTextfieldArray addObject:valueTextField];
}
if ([allTextfieldArray count]>0) {
UITextField *txtField = [allTextfieldArray lastObject];
[allTextfieldArray removeLastObject];
[txtField removeFromSuperview];
txtField = nil;
UITextField *txtField2 = [allTextfieldArray lastObject];
[allTextfieldArray removeLastObject];
[txtField2 removeFromSuperview];
txtField2 = nil;
yAxisDistance = yAxisDistance-35;
}
you store the last textField in your variables keyTextField and valueTextField. So when you call your deleteTextField the last both will be deleted. you must track which textFileds you want exactly to delete.
for example you could give all your textFields a tag number when you create them:
first create an int as counter:
#implementation YourClass {
int tagcounter;
}
in init method set your counter:
tagcounter = 0;
in your addTextField:
keyTextField.tag = tagcounter++;
valueTextField.tag = tagcounter++;
when the delete button is tapped, you must know the textfield tags and pass them to your deleteTextField method. There you could do something like:
-(void)deleteTextFieldWithTag:(int)tagnumber {
for (UIView *view in [self.view subviews])
{
if ([view isKindOfClass:[UITextField class]])
{
if (view.tag == tagnumber || view.tag == tagnumber+1) {
[view removeFromSuperview];
}
}
}
I want users to be able to use pinch to zoom in my app which uses Storyboard and programmatically generated buttons and labels to produce a View within a Viewcontroller. In the View's Attribute inspector I have checked both User Interaction Enabled and Multiple Touch. But no pinch to zoom seems to work.
The code which creates the important part of the View is as follows.
- (void)viewDidLoad
{
[super viewDidLoad];
NSInteger xPipsOrigin = 0;
NSInteger xPipsStep = 22.0;
NSString* cards = #"AKQJT98765432";
NSInteger yPipsOrigin = 100;
if (UI_USER_INTERFACE_IDIOM()== UIUserInterfaceIdiomPhone){
xPipsOrigin = 0;
xPipsStep = 22.0;
xPipsStep = 34.0;
}
else{
xPipsOrigin = 100;
xPipsStep = 40.0;
}
NSInteger xPipsCurrent = xPipsOrigin;
self.countCards = 0;
self.cardList = [NSMutableArray arrayWithCapacity:0];
yPipsOrigin = 100;
xPipsCurrent = xPipsOrigin;
for( int x=0;x<[cards length]; x++ ){
[cards substringWithRange:NSMakeRange(x,1)];
UIButton *b= [UIButton buttonWithType:UIButtonTypeRoundedRect];
xPipsCurrent += xPipsStep;
[b setTitle:[cards substringWithRange:NSMakeRange(x,1)] forState:UIControlStateNormal];
[b setTitle:#" " forState:UIControlStateDisabled];
[b setFrame:CGRectMake(xPipsCurrent, yPipsOrigin, 20, 20)];
[b setEnabled:YES];
[b setUserInteractionEnabled:YES];
[self.view addSubview:b];
[b addTarget:self action:#selector(spadeButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
}
xPipsCurrent = xPipsOrigin + xPipsStep/2;
for( int x=0;x<[cards length]-1; x++ ){
xPipsCurrent += xPipsStep;
UILabel *lab = [[UILabel alloc] initWithFrame:CGRectMake( 0, 0, 20, 20)];
lab.text = #"\u2660";
lab.backgroundColor = [UIColor clearColor];
lab.center = CGPointMake(xPipsCurrent+10, yPipsOrigin+10);
[self.view addSubview:lab];
}
}
How can I enable Pinch to Zoom in this app?
If you want to enable pinch to zoom on a UIView you have to wrap it into a UIScrollView first
Whilst you can use User Interaction Enabled and try do it that way, it'll be easier to add a UIGestureRecogniser to your UIView. That way it handles most of the setup code for you, and when it detects a pinch triggers a method.
Basic example in Objective-C:
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handlePinchGesture:)];
[self.view addGestureRecognizer:pinchGesture];
- (void)handlePinchGesture:(id)sender
{
NSLog(#"Recieved pinch");
UIPinchGestureRecognizer *senderGR = (UIPinchGestureRecognizer *)sender;
NSLog(#"Scale is: %f", senderGR.scale);
}
And in Swift:
let pinchGestureRecognizer = UIPinchGestureRecognizer.init(target: self, action: #selector(ViewController.handlePinchGesture(_:)))
self.view.addGestureRecognizer(pinchGestureRecognizer)
func handlePinchGesture(sender:AnyObject) {
print("Received pinch")
let senderGR = sender as! UIPinchGestureRecognizer
print("Scale is \(senderGR.scale)")
}