I can use
-(BOOL)textViewShouldBeginEditing:(UITextView *)firstTextView {
return NO;
}
to stop a textView from being edited via the keyboard.
However if I try to use another
-(BOOL)textViewShouldBeginEditing:(UITextView *)secondTextView {
return NO;
}
I get an error for the redefinition of the textViewShouldBeginEditing.
I am sure it is something silly I am doing with the delegate method.
The error is telling you precisely what's wrong. You're redefining the method.
The method takes a UITextView* as an argument precisely so you can tell which text view is asking the question, so you can tailor your response. Of course if you want to always say NO to all text views then you can ignore the parameter. In any case, just delete your duplicate definition and you'll be fine.
Related
Say I write a UITextField subclass and want to have control over the text written into it by the user. I would set the input field's delegate to be myself and implement -textField:shouldChangeCharactersInRange:replacementString:.
However, I would still want to allow whatever part of code uses me as a text field to implement the usual delegate methods. An approach for that would be to store a second delegate reference and map them like so:
- (id)init {
self = [super init];
super.delegate = self;
return self;
}
- (void)setDelegate:(id)delegate {
self.nextDelegate = delegate;
}
- (id)delegate {
return self.nextDelegate;
}
I would then proceed to implement all UITextFieldDelegate methods and forward them to the next delegate as I wish. Obviously, I may want to modify some parameters before passing them on to the next delegate, like in -textField:shouldChangeCharactersInRange:replacementString:.
Another problem I'm thinking of is when the user's sets nextDelegate to the text field itself (for whatever reason), resulting in an infinite loop.
Is there a more elegant way to hijack delegate callbacks like in the example code I posted?
The problem with your approach is the overridden delegate accessor: There's no guarantee that Apple's code always uses the delegate ivar directly and does not use the getter to access the delegate. In that case it would just call through to the nextDelegate, bypassing your sneaked in self delegate.
You might have checked that your approach works in the current implementation but this could also change in future UIKit versions.
Is there a more elegant way to hijack delegate callbacks like in the example code I posted?
No, I'm not aware of any elegant solutions. You could not override the delegate accessor and instead set up secondary delegate (to which you have to manually pass all delegate messages).
To solve the actual problem of filtering text input it might be worthwhile looking into
- (void)replaceRange:(UITextRange *)range withText:(NSString *)text;
This method is implemented by UITextField (as it adopts UITextInput) and could be overridden to filter the text argument.
I think you're thinking about this correctly, and the approach you outlined will work fine (I've done it).
There's no circularity issue because you shouldn't expose nextDelegate in the subclass's public interface, so no caller will have the chance to setup a cycle. (You could also test in the setter that delegate != self.
It would be better, though, if you could avoid this altogether. For example, if you just want to tweak the text field text as it changes, you can get the control event:
[self addTarget:self action:#selector(didChange:) forControlEvents:UIControlEventEditingChanged];
Then,
- (void)textFieldDidChange:(id)sender {
self.text = [self alteredText];
}
- (NSString *)alteredText {
// do whatever transform to user input you wish, like change user input 'a' to 'x'
return [self.text stringByReplacingOccurrencesOfString:#"a" withString:#"x"];
}
This will work as well, but with the odd side effect that the delegate won't see the alteredText in shouldChangeCharactersInRange:. That's fixable by making alteredText public and having the class customers call it instead of the standard getter.
All of the problems with subclassing can be avoided by using a different approach of intercepting delegate messages: A "delegate proxy".
The idea is to use an intermediate object (derived from NSProxy) that either responds to a delegate message or passes it along to the next delegate. It's basically what you did by subclassing the UITextField but instead of using the text field object we'll use a custom object that handles only the interception of some delegate messages.
These customized delegate proxys form a set of reusable building blocks which are simply plugged into each other to customize the behavior of any object that uses delegation.
Here's an example (code on github) of a chain of delegates:
UITextField -> TextFilterDelegate -> SomeViewController
The UITextField passes delegate messages to TextFilterDelegate which responds to textField:shouldChangeCharactersInRange:replacementString: and passes other delegate messages on to its own delegate (the view controller).
What's the difference between declaring a UIButton in Xcode like this:
- (IBAction)testButton;
and declaring a button like this:
- (IBAction)testButton:(id)sender;
I understand that in the .m file you would then implement the buttons accordingly, as shown below:
- (IBAction)testButton
{
// insert code here..
}
and setting it up like this:
- (IBAction)testButton:(id)sender
{
// insert code here..
}
Is there any additional things you can do by declaring the button with :(id)sender, is there some additional stability, or is there no difference?
With :(id)sender you are able to access the button itself through the sender variable. This is handy in many situations. For example, you can have many buttons and give each a tag. Then use the [sender tag] method to find which button was tapped if many buttons are using this IBAction.
- (IBAction)someMethod:(id)sender {
// do stuff
}
Using (id)sender, you have a reference to who sent the method call. Please note, this doesn't have to be limited to a UIButton.
If you're created this method via control-dragging from the storyboard an only hooking up a single button, then sender is basically useless (it will always be the same), and should probably be marked as unused:
#pragma unused (sender)
(The compiler can better optimize your code if you do this.)
However, there's nothing wrong with hooking up several UI elements to the same IBAction method. You can then distinguish the sender via:
[sender tag]
...which returns an int that was either set via the storyboard or programmatically.
Moreover, you can call this method elsewhere in your class. You can either pass nil as the sender, or you can pass it a particular UI element in order to force it into the results you've coded for objects of that tag.
Nonetheless, if you plan to call the method with a nil argument, you can always throw:
if(!sender)
... into the method in order to handle special logic for when the method has been invoked programmatically as opposed to via user interaction.
It allows you to know which button you are working with. I have posted a simple example for a card game below
- (IBAction)flipCard:(id)sender {
[self.game flipCardAtIndex:[self.cardButtons indexOfObject:sender]];
self.flipCount++;
[self updateUI];
}
This method is used for a card flipping game. There are multiple buttons on the screen representing different cards. When you hit the button, a card in the model must be flipped. We know which one by finding the index of the variable sender
I have an issue and here how it goes,
I have a view with a subview, the subview is loaded conditionally, only if the parent view is setHidden property is set to YES;
something like [parentView setHidden:YES] and if([parentView isHidden]),
I want to call a method when the orientation changes and that is the cited snippet above, but I have observed that the method shouldAutorotateToInterfaceOrientation is called 4 times during loading and 2 times during runtime, since the method is called more than once, how can I possibly implement a method call ideally since apple's existing method doesn't seem to give me the intuitiveness to put my custom method call with the existing method.
If I would hack this thing, it is possible, but somebody might have a better idea before resorting to things that in the future would just cause me more trouble than benefit.
TIA
Have you tried with
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
// check here for your desired rotation
}
I do not want the user to be able to select the first few characters of my UITextView. I have tried subclassing it and noticed methods such as -setSelectedRange: and -setSelectedTextRange:. Both are called at different times but it seems like it's the latter that I need.
The -setSelectedTextRange: method takes a UITextRange object, which has a UITextPosition property called "start". This sounds like what I want but I cannot write to it, and there are no classes for this object.
Does anyone have any ideas on how I can do this? FWIW, I'm trying to replicate what Facebook have on their "Check-In" view on their iPhone app.
Thanks in advance!
I'm not personally familiar with the functionality of the Facebook app Check-In view, but based on your description, it sounds like you need something like this in your subclass:
- (BOOL)becomeFirstResponder
{
if ([super becomeFirstResponder]) {
// Select text in field.
[self setSelectedTextRange:[self textRangeFromPosition:[self positionFromPosition:self.beginningOfDocument offset:1] toPosition:self.endOfDocument]];
return YES;
}
return NO;
}
In particular, note the "offset:1" argument. You should be able to use this to set the start of your selected text range. Also, you'll want to make sure that the new text range you specify is valid for the number of characters that are in the text field.
Say for instance I have an IBAction that is hooked up to a UIButton in interface builder.
- (IBAction)functionToBeCalled:(id)sender
{
// do something here
}
With-in my code, say for instance in another method, what is the best way to call that IBAction?
If I try to call it like this, I receive an error:
[self functionToBeCalled:];
But, if I try to call it like this (cheating a bit, I think), it works fine:
[self functionToBeCalled:0];
What is the proper way to call it properly?
The proper way is either:
- [self functionToBeCalled:nil]
To pass a nil sender, indicating that it wasn't called through the usual framework.
OR
- [self functionToBeCalled:self]
To pass yourself as the sender, which is also correct.
Which one to chose depends on what exactly the function does, and what it expects the sender to be.
Semantically speaking, calling an IBAction should be triggered by UI events only (e.g. a button tap). If you need to run the same code from multiple places, then you can extract that code from the IBAction into a dedicated method, and call that method from both places:
- (IBAction)onButtonTap:(id)sender {
[self doSomething];
}
This allows you to do extra logic based on the sender (perhaps you might assign the same action to multiple buttons and decide what to do based on the sender parameter). And also reduces the amount of code that you need to write in the IBAction (which keeps your controller implementation clean).