NSAttributedString with a URL not working - ios

In a swift iOS app, I have set a UILabel with an NSAttributedString on it.
The AttributedString contains a URL set with
[NSLinkAttributeName:NSURL(string:urlStr)!].
All appears fine, meaning that the URL is clearly set as such, but when I tap it nothing happens.
Is there something I should do to make the URL work as one would expect (i.e fire Safari)?
I tried the following but it had no effect at all.
theLabel.userInteractionEnabled = true

NSLinkAttribute is not working with UILabel as the handler method will respond to UItextViewDelegate Only.
You can either use. TTTAttributedLabel or can go with Replacing your UILabel with UITextView. After replacing the same use
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
And don't forget to check for Detection and Behaviour property.

Try this code :
var str: NSMutableAttributedString = NSMutableAttributedString(string: "Google")
str.addAttribute(NSLinkAttributeName, value: "http://www.google.com", range: NSMakeRange(0, str.length))
yourLabel.attributedText = str
This is not directly about the question but just to clarify, UITextField and UILabel does not support opening URLs. If you want to use UILabel with links you can check TTTAttributedLabel.
Also you should set dataDetectorTypes value of your UITextView to UIDataDetectorTypeLink or UIDataDetectorTypeAll to open URLs when clicked. Or you can use delegate method as suggested in the comments.

Related

Set UITextField Keyboard With Created Keyboard

in my application, I have created two targets: one is for the actual application and the other is for my custom keyboard. In my actual application, I have a UITextField, which the keyboardType = .numberPad. My issue now is that I am trying to set the keyboardType property of my UITextField with my custom keyboard. Anyway how I can do that?
Please try this :
textField.inputView = myCustomKeyboard().inputView
You can do myTextField.keyboardType = .numberPad. That will make the keyboard a number pad for that specific UITextField.

Select specific text in UITextView to fire a segue

I’m trying to add a function to my application where the user can select a PRESPECIFIED text by me in order to fire a segue and get an explanation/information about what the user has selected.
The following text is an example:
Hawaii is the most recent of the 50 "U.S. states" and joined the Union on August 21, 1959.
The user can select "U.S. states" and will get information about U.S. states.
So Can I do that with Xcode or do I have to work with some html editing application? and if so, where would be a good place (online or book) to start learning how to do such a thing?
Thanks.
There may be better ways, but I know of one way to do what you're asking.
Create a NSMutableAttributedString with the text you want shown
Determine the range of characters you want to be clickable and using setAttributes:rangeSet, add the NSLinkAttributeName attribute with a value of a string you can use to identify the clicked region.
Create a UITextView and set the attributedString property to the string from above
Set some object as the delegate of your UITextView
Override the textView:shouldInteractWithURL:inRange: method. Return NO here, but do what you want with the URL, created from the string you used before.
Here's the code part. I'll leave the UITextView creation to you.
-(void)viewDidLoad
{
[super viewDidLoad];
NSMutableAttributedString *attString = self.textView.attributedText.mutableCopy;
[attString setAttributes:#{NSLinkAttributeName:#"123"} range:NSMakeRange(0, 10)];
self.textView.attributedText = attString;
self.textView.delegate = self;
}
- (BOOL)textView:(UITextView *)textView shouldInteractWithURL:(NSURL *)URL inRange:(NSRange)characterRange
{
NSLog(#"%#", URL);
return NO;
}
You can do it in Xcode. You will want to create a segue in the storyboard and add an identifier to the segue. Then in one of the UITextViewDelegate methods, you can check the text in the text view. If it matches, you can call the segue programmatically using -performSegueWithIdentifier:sender:. Alternatively, you can create the entire segue programmatically using -segueWithIdentifier:source:destination:performHandler:.

Is there a delegate call for when the text is changed on a UITextView?

When I set my UITextView programmatically like this:
[self.textView setText:#""];
The delegate method textViewDidChange: does not get called. Is there a way I can find that without making a UITextView subclass?
When manually setting the text of a UITextView with code, the textViewDidChange: method does not get called. (If you have your text view's delegate set, it will get called when the user edits it, though.)
One possible workaround would be to manually call textViewDidChange: anytime you edit the text. For example:
[self.textView setText:#""];
[self textViewDidChange:self.textView];
Kind of a hackish way of doing it, but it gets the job done.
I upvoted #rebello95's response because it is one approach. But another, less hacky approach is to do as
- (void)whereIManuallyChangeTextView
{//you don't actually have to create this method. It's simply wherever you are setting the textview to empty
[self.textView setText:#""];
[self respondToChangeInTextView:self.textView];
}
- (void)textViewDidChange:(UITextView *)textView
{
//...some work and then
[self respondToChangeInTextView:textView];
}
- (void)respondToChangeInTextView:(UITextView *)textView
{
//what you want to happen when you programmatically/manually or interactively change the textview
}
This snippet exemplifies a respectable pattern that will make your code more readable.
In swift you could override the text variable from the UITextView class:
class MyTextView: UITextView {
override public var text: String? {
didSet {
self.textViewDidChange(self)
}
}
}
Old post, but I had the same problem and thought I would share my solution (in Swift).
textViewDidChange(_ textView: UITextView) does not get called by just setting the text property, but it does get called when using replace(range: UITextRange, withText: String). So you need to create a UITextRange for the entire string of the UITextView and replace it with a new string.
// Create a range of entire string
let textRange = textView.textRange(from: textView.beginningOfDocument, to: textView.endOfDocument)
// Create a new string
let newText = ""
// Call Replace the string in your textView with the new string
textView.replace(textRange!, withText: newText)
That should do it. Of course, you need to set up UITextViewDelegate for this to work:
class ViewController: UIViewController, UITextViewDelegate {
You could also subclass UITextView and override setText to include
[self textViewDidChange:self.textView]
so that you don't have to call it every time you set the text of your UITextView.
delegate method textDidChange does not respond to programmatical changes in textes, you can use observation to get notified
declare your text view variable with #objc dynamic
declare and hold a variable with type NSKeyValueObservation
use function observe(_:changeHandler:) bind your text view's text property, hold the return value with variable declared in step 2
observe changes in changeHandler
example:
#objc dynamic private var textView: UITextView!
private var observation: NSKeyValueObservation?
func bind() {
observation = observe(\.textView.text, options: [.old, .new]) { object, change in
print(object, change)
}
}
use this instead: (this won't reset the current text)
[self.textView insertText:#"something"];
this will call the delegate and will add text where the cursor is. Of course if you want to reset the whole text you can either:
[self.textView setText:#""];
[self textViewDidChange:self.textView];
or
[self.textView setText:#""];
[self.textView insertText:#"something"];

UITextview with zooming?

how can i achieve UITextview with zooming facility?any tutorial please?
You can make simple UIWebView and it will be like UITextView, but with zooming.
This way has a lot of advantages. For example bold text, or italic. You will be able to use different fonts in one text.
Step by step guide:
create UIWebView
generate html string. For example: <html><head></head><body>My text</body></html>
load string in UIWebView
If I'm understanding your question correctly, you want a UITextView, and you want to allow the user to use two fingers to zoom in? If that's the case, when you add the UITextView in IB, at the bottom of the settings for the UITextView there is a check box for "Multiple Touch" just check that box and it will allow the user to zoom in. Hope I understood your question correctly.
If you not need user to edit text and just need to show some text with ability to change font attributes (i.e. size, weight) - use attributed string of text view. Then allow editing attributes and just call methods from UIResponderStandardEditActions protocol, like increaseSize: or decreaseSize: on UITextView. Before it you should set a range of text which must be affected:
self.textView.editable = NO;
self.textView.selectable = NO;
self.textView.allowsEditingTextAttributes = YES;
self.textView.attributedText = [[NSAttributedString alloc] initWithString:#"Lorem ipsum dolor sit er elit lamet..."];
Before calling increase/decrease method select all text:
//First select needed range
[self.textView setSelectedRange:NSMakeRange(0, self.textView.attributedText.length)];
//Then call increazing method
[self.textView increazeSize:nil];
Also you can call other methods of that nice protocol, which UITextView kindly implement:
[self.textView decreaseSize:sender];
[self.textView toggleBoldface:nil];
[self.textView toggleItalics:nil];
[self.textView toggleUnderline:nil];

How to create a multiline UITextfield?

I am developing an application where user has to write some information. For this purpose I need a UITextField which is multi-line (in general UITextField is a single line).
As I'm Googling I find a answer of using UITextView instead of UITextfield for this purpose.
UITextField is specifically one-line only.
Your Google search is correct, you need to use UITextView instead of UITextField for display and editing of multiline text.
In Interface Builder, add a UITextView where you want it and select the "editable" box. It will be multiline by default.
You can fake a UITextField using UITextView. The problem you'll have is that you lose the place holder functionality.
If you choose to use a UITextView and need the placeholder, do this:
In your viewDidLoad set the color and text to placeholders:
myTxtView.textColor = .lightGray
myTxtView.text = "Type your thoughts here..."
Then make the placeholder disappear when your UITextView is selected:
func textViewDidBeginEditing (textView: UITextView) {
if myTxtView.textColor.textColor == ph_TextColor && myTxtView.isFirstResponder() {
myTxtView.text = nil
myTxtView.textColor = .white
}
}
When the user finishes editing, ensure there's a value. If there isn't, add the placeholder again:
func textViewDidEndEditing (textView: UITextView) {
if myTxtView.text.isEmpty || myTxtView.text == "" {
myTxtView.textColor = .lightGray
myTxtView.text = "Type your thoughts here..."
}
}
Other features you might need to fake:
UITextField's often capitalize every letter, you can add that feature to UITableView:
myTxtView.autocapitalizationType = .words
UITextField's don't usually scroll:
myTxtView.scrollEnabled = false
Ok I did it with some trick ;) First build a UITextField and increased it's size like this :
CGRect frameRect = textField.frame;
frameRect.size.height = 53;
textField.frame = frameRect;
Then build a UITextView exactly in the same area that u made my UITextField, and deleted its background color. Now it looks like that u have a multiple lines TextField !
Besides from the multiple line behaviour, the main difference between UITextView and UITextField is that the UITextView does not propose a placeholder. To bypass this limitation, you can use a UITextView with a "fake placeholder."
See this SO question for details: Placeholder in UITextView.
If you must have a UITextField with 2 lines of text, one option is to add a UILabel as a subview of the UITextField for the second line of text. I have a UITextField in my app that users often do not realize is editable by tapping, and I wanted to add some small subtitle text that says "Tap to Edit" to the UITextField.
CGFloat tapLlblHeight = 10;
UILabel *tapEditLbl = [[UILabel alloc] initWithFrame:CGRectMake(20, textField.frame.size.height - tapLlblHeight - 2, 70, tapLlblHeight)];
tapEditLbl.backgroundColor = [UIColor clearColor];
tapEditLbl.textColor = [UIColor whiteColor];
tapEditLbl.text = #"Tap to Edit";
[textField addSubview:tapEditLbl];
Yes, a UITextView is what you're looking for. You'll have to deal with some things differently (like the return key) but you can add text to it, and it will allow you to scroll up and down if there's too much text inside.
This link has info about making a screen to enter data:
create a data entry screen
'override func viewDidLoad(){
super.viwDidLoad()
YOUR_TEXT_VIEW.textColor = UIColor.gray // YOUR PREFERRED PLACEHOLDER COLOR HERE
YOUR_TEXT_VIEW.text = "YOUR DEFAULT PLACEHOLDER TEXT HERE"
YOUR_TEXT_VIEW.delegate = self
}'
This code block is enough. Please don't forget to set delegate in viewDidLoad or by storyboard just before to use the following extension:
extension YOUR_VIEW_CONTROLLER: UITextViewDelegate {
func textViewDidBeginEditing (_ textView: UITextView) {
if YOUR_TEXT_VIEW.text.isEmpty || YOUR_TEXT_VIEW.text == "YOUR DEFAULT PLACEHOLDER TEXT HERE" {
YOUR_TEXT_VIEW.text = nil
YOUR_TEXT_VIEW.textColor = .red // YOUR PREFERED COLOR HERE
}
}
func textViewDidEndEditing (_ textView: UITextView) {
if YOUR_TEXT_VIEW.text.isEmpty {
YOUR_TEXT_VIEW.textColor = UIColor.gray // YOUR PREFERED PLACEHOLDER COLOR HERE
YOUR_TEXT_VIEW.text = "YOUR DEFAULT PLACEHOLDER TEXT HERE"
}
}
}
use UITextView instead of UITextField
A supplement to h4xxr's answer in the above, an easier way to adjust the height of the UITextField is to select square border style in the attribute inspectors->Text Field. (By default, the border style of a UITextfield is ellipse.)
Reference: Answered Brian in here : How to set UITextField height?
Use textView instead then conform with its delegate, call the textViewDidChange method inside of that method call tableView.beginUpdates() and tableView.endUpdates() and don't forget to set rowHeight and estimatedRowHeight to UITableView.automaticDimension.
There is another option that worked for me:
Subclass UITextField and overwrite:
- (void)drawTextInRect:(CGRect)rect
In this method you can for example:
NSDictionary *attributes = #{ NSFontAttributeName : self.font,
NSForegroundColorAttributeName : self.textColor };
[self.text drawInRect:verticalAlignedRect withAttributes:attributes];
This code will render the text using as many lines as required if the rect has enough space. You could specify any other attribute depending on your needs.
Do not use:
self.defaultTextAttributes
which will force one line text rendering

Resources