Attributed Text View that opens address in new map view - ios

I'm working on an app that uses parse as the backend and allows users to post their address via text view. I enabled the "Addresses" attribute and addresses open in the Apple maps app. Instead I'd like to keep my users in my app by opening a new view with a map and the address they tapped pinned to the map view.
Is there anyway I can do this without making things overly complicated?
Thanks for your help!

You can implement the UITextViewDelegate method:
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange;
Description: Asks the delegate if the specified text view should allow
user interaction with the given URL in the given range of text.
The text view calls this method if the user taps or long-presses the
URL link. Implementation of this method is optional. By default, the
text view opens the application responsible for handling the URL type
and passes it the URL. You can use this method to trigger an
alternative action, such as displaying the web content at the URL in a
web view within the current application.
Reference
Ex:
Make sure your class conforms to: <UITextViewDelegate>
Set your textview delegate: [textView setDelegate:self];
Implement the delegate method:
...
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange {
// Open custom map using data from textView.text.
}

Related

iOS 12 - oneTimeCode OTP delegate

Is there any delegate getting fired when the user taps the OTP suggestion that iOS provides?
I know that I could use UITextFieldDelegate methods to detect changes in the textfield's text property, but knowing that the user has pressed the button would make things so much easier...
According to Apple's article: About the Password AutoFill Workflow
For iOS apps, the system always sends a textDidChangeNotification notification when a view has been modified. It also calls one of the delegate methods of the view—but the exact method depends on the view’s type:
UITextField: The system calls your UITextFieldDelegate object’s textField(_:shouldChangeCharactersIn:replacementString:) method.
UITextView: The system calls your UITextViewDelegate object’s textView(_:shouldChangeTextIn:replacementText:) method.
Custom View adopting the UITextInput protocol: The system calls the insertText(:) method or replace(:withText:) in the UIKeyInput protocol.
You can create a subclass of UITextField or UITextView and override insertText(:) method and replace(:withText:) method, it will be called when user tap quickbar.
In my case, I found that when I got a text in Should change Charater I got two empty spaces, there I come to know that it is autofill characters, so according to it, I update my four text field one by one.

Cascades of delegates and hijacking delegate callbacks in Objective-C

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).

nil object in iOS8 delegate methods - custom keyboards

I'm building a custom keyboard
and I'm implementing the following delegate methods in my InputViewController.
But I always get _textInput = nil_
- (void)textWillChange:(id<UITextInput>)textInput
- (void)textDidChange:(id<UITextInput>)textInput
- (void) selectionWillChange:(id<UITextInput>)textInput
- (void) selectionDidChange:(id<UITextInput>)textInput
Does anybody know how to fix it?
Is it nil for a reason?
Do I need to implement something by myself?
Good question. But it seems that UITextInputDelegate is not a protocol that you implement.
From Apple Docs titled Lower Level Text-Handling Technologies:
When changes occur in the text view due to external reasons—that is,
they aren't caused by calls from the text input system—the UITextInput
object should send textWillChange:, textDidChange:,
selectionWillChange:, and selectionDidChange: messages to the input
delegate (which it holds a reference to). For example, when users tap
a text view and you set the range of selected text to place the
insertion point under the finger, you would send selectionWillChange:
before you change the selected range, and you send selectionDidChange:
after you change the range.
And from the docs on UITextInputDelegate:
The UIKit provides a private text input delegate, which it assigns at
runtime to the inputDelegate property of the object whose class adopts
the UITextInput protocol.
The implication of the above is that we don't implement these delegate methods; we use them to inform the inputDelegate that you have changed your text or selection via means other than keyboard input.
Here is an example method that illustrates this:
- (void)delete:(id)sender;
{
if (selection && ![selection isEmpty]) {
[inputDelegate textWillChange:self];
[inputDelegate selectionWillChange:self];
[self replaceRange:selection withText:#""];
[inputDelegate selectionDidChange:self];
[inputDelegate textDidChange:self];
}
}
Sample code with more examples here.

UIWebView inside UICollectionViewCell does not call Delegate Method

I am currently embedding a UIWebView inside a UICollectionViewCell to be able to view HTML formatted text residing in my local data structure.
I know it has been stated many times one should not embed an UIWebView inside a scrollView, but i currently see no other way to achieve displaying mixed content overviews (That is the current requirement, and so far UICollectionView does work very well with large data sets).
The Problem is, that i am not able to call the UIWebViews Delegate method:
- (BOOL)webView:(UIWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType { ... }
I need the delegate method to be able to display external links or call local methods.
Is there a way to achieve this? Maybe by handing over the events programatically?
Thanks for the feedback!
I just found out what i did wrong:
I just needed to update the cells' contentView: [[cell contentView] setFrame:frameContainingWebViewSize]
That did the trick. The delegate method was not called because the contentView did not cover the webview (clipSubviews is set to NO) and thus the tap events where not fired.

How to pass 2 parameters in Objective-C

In iOS application development, how do I pass 2 parameters to login button?
I want to pass username and password on the button click of login button.
I have given in this manner:
-(IBAction)Login_Method:(id)sender withpassword:(id)password
{
}
It is not possible to send 2 parameters in an IBAction method.
What you can do is
Create 2 outlets and then connect these to password and username textfields.
On Clicking the login button, read the text value from the above 2 outlets.
Your login button action method will be like this
//username and password are UITextfields
-(IBAction)Login_Method:(id)sender
{
id name=[username text];
id pass=[password text];
}
As far as I know, IBActions are defined this way:
- (IBAction) actionMethod:(id)sender
Unless your button is subclassed much differently than usual, it is not going to call IBAction with an extra parameter (your withpassword: password).
You will have to use the standard definition above, get the call from the UIButton, and then get the text from your fields and then you can pass that to another method that will do the login with those two fields - unrelated to the UIButton's IBAction.
Typically, when you link a UIButton to a method in Interface Builder, that method will have no parameters. Technically, it can have zero, one, or two. The first is the sender (the button that was tapped) and the second is the event (the tap).
See the target-action mechanism for controls:
- (void)action
- (void)action:(id)sender
- (void)action:(id)sender forEvent:(UIEvent *)event
Also, you will typically link user inputs (such as UITextField instances) as IBOutlets. Then, you can access the user input through the text property:
// in your interface (header file):
IBOutlet UITextField *userNameTextField;
// in your implementation file:
NSString *userName = userNameTextField.text;
Finally, you should use correct types. Usernames and passwords are probably NSString* (pointers to NSString objects). "id" is the generic type and is not necessary here.

Resources