I'm using iOS 11 and Swift 4.
I am trying to programmatically generate a link inside a UITextView.
All the attributes work (i.e. color, size, range etc.) - but unfortunately, the link does not work. Can anybody tell me why?
Here is my code:
#IBOutlet weak var textView: UITextView!
// Setting the attributes
let linkAttributes = [
NSAttributedStringKey.link: URL(string: "https://www.apple.com")!,
NSAttributedStringKey.font: UIFont(name: "Helvetica", size: 18.0)!,
NSAttributedStringKey.foregroundColor: UIColor.blue
] as [NSAttributedStringKey : Any]
let attributedString = NSMutableAttributedString(string: "Just click here to do stuff...")
// Set the 'click here' substring to be the link
attributedString.setAttributes(linkAttributes, range: NSMakeRange(5, 10))
self.textView.delegate = self
self.textView.attributedText = attributedString
Again, the link seems correct, but clicking it does not work.
Here is the screenshot:
You should enable isSelectable and disable isEditable for your text view.
textView.isSelectable = true
textView.isEditable = false
You can also set up these behaviors inside Interface Builder in the Behavior section of the text view's Attributes inspector:
Related
I need to implement a text editor using UITextView that supports:
Bold/Italic/Underline
Color,Font,font size changes
Paragraph alignment
List format (bullets, numbers, etc.)
Custom selection of text anywhere in the text view and change the properties
So far I have managed to do it without NSTextStorage but it seems I am hitting limits. For instance, to change font, I use UIFontPickerViewController and change the font as follows:
func fontPickerViewControllerDidPickFont(_ viewController: UIFontPickerViewController) {
if let selectedFontDesc = viewController.selectedFontDescriptor {
let font = UIFont(descriptor: selectedFontDesc, size: selectedFontDesc.pointSize)
self.selectedFont = font
self.textView.typingAttributes = [NSAttributedString.Key.foregroundColor: self.selectedColor ?? UIColor.white, NSAttributedString.Key.font: self.selectedFont ?? UIFont.preferredFont(forTextStyle: .body, compatibleWith: nil)]
if let range = self.textView.selectedTextRange, let selectedFont = selectedFont {
let attributedText = NSMutableAttributedString(attributedString: self.textView.attributedText)
let location = textView.offset(from: textView.beginningOfDocument, to: range.start)
let length = textView.offset(from: range.start, to: range.end)
let nsRange = NSRange(location: location, length: length)
attributedText.setAttributes([NSAttributedString.Key.font : selectedFont], range: nsRange)
self.textView.attributedText = attributedText
}
}
}
This works but the problem is it resets the color of the selected text and other properties. I need to understand a way in which the existing attributed of the text under selection are not disturbed. I suspect the way to do is with using NSTextStorage but I can't find anything good on internet that explains the right use of NSTextStorage to achieve this.
The problem is this call:
attributedText.setAttributes...
This, as you have observed, makes the attribute you provide (here, the font) the only attribute of this range. Instead, you want to add your font attribute to the existing attributes:
https://developer.apple.com/documentation/foundation/nsmutableattributedstring/1414304-addattributes
I want to change the label.text on my story board. The only way I know how to do it now is by clicking the label and on attribute fields change it, or changing it is when a button pressed.
But I want to change the .text on the story board. Sorry for the confusion, see the picture to make it more understandable.
Sounds like you just need to add the change code to your viewDidLoad():
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
titlebar.text = "New Title"
}
Answer has been updated for Swift 4.2
General form of setting and getting attributed string is given below
// create attributed string
let myString = "Swift Attributed String"
let myAttribute = [ NSAttributedString.Key.foregroundColor: UIColor.blue ]
let myAttrString = NSAttributedString(string: myString, attributes: myAttribute)
// set attributed text on a UILabel
myLabel.attributedText = myAttrString
I've created a custom info button that I want to put inside a regular UILabel.
The idea is to have the screen say "Tap the (BUTTON HERE) for more information". Is there a way to do this without creating two UILabels? And if creating 2 labels is the only way, how can I put everything on one line?
I tried to do (button) inside the label.text but that shows the button's properties instead of placing the button. I also tried label.addSubview(button) which works but adds the button in the wrong place.
The best way to do this is using a UITextView with an NSAttributedString, where one of the attributes is your link.
let textView = UITextView()
textView.delegate = self
// These allow the link to be tapped
textView.isEditable = false
textView.isScrollEnabled = false
// Removes padding that UITextView uses, making it look more like a UILabel
textView.textContainer.lineFragmentPadding = 0.0
textView.textContainerInset = .zero
Then for the NSAttributedString
let text = "Tap HERE for more information"
let linkText = "HERE"
// Get range for tappable section
let linkRange = (text as NSString).range(of: linkText)
// Styling
let attributes: [NSAttributedString.Key: Any] = [
.foregroundColor: UIColor.black
]
// Actual Link!
let linkTextAttributes: [NSAttributedString.Key: Any] = [
.underlineStyle: NSUnderlineStyle.single.rawValue,
.link: "https://www.example.com" //The link you want to link to
]
let attributedString = NSMutableAttributedString(string: text, attributes: attributes)
attributedString.addAttributes(linkTextAttributes, range: linkRange)
Then use these UITextView delegate functions
// Removes a lot of the actions when user selects text
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool {
return false
}
// Handle the user tapping the link however you like here
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
viewModel.urlTapped(URL)
return false
}
I am creating a app in which i use several UITextfield's. I know how to change the placeholder colour of a single text field.
textField.attributedPlaceholder = NSAttributedString(string: "Username", attributes: [NSForegroundColorAttributeName: UIColor.redColor()]).
But i want to change the placeholder colour of all the UITextFields in the whole app. My app has more than 50 UIViewControllers and more than 25 of them has textFields(2 to 22 per screen). I want a code that can be used globally in one place so that i don't need to go to every view controller and change it manually.
If you have any other alternatives to make the job done please let me know.
I am using xcode 7.1.1 swift 2.0
Update:
For default the Placeholder colour is set to light grey colour. Is there any way for us to tweak that default behaviour and change it to any other colour?
How can we access this default code and change it?
Create the extension method
extension String {
func toAttributedString(font font:UIFont!, kerning: CGFloat!, color:UIColor!) -> NSAttributedString {
return NSAttributedString(string: self as String, font: font, kerning: kerning, color: color)!
}
}
extension NSAttributedString {
convenience init?(string text:String, font:UIFont!, kerning: CGFloat!, color:UIColor!) {
self.init(string: text, attributes: [NSKernAttributeName:kerning, NSFontAttributeName:font, NSForegroundColorAttributeName:color])
}
}
Example usage
/ Example Usage
var testString: String = "Hi Kautham"
var testAttributedString: NSAttributedString = testString.toAttributedString(font: UIFont.boldSystemFontOfSize(20), kerning: 2.0, color: UIColor.whiteColor())
let label = UILabel(frame: CGRect(x: 50, y: 50, width: 200, height: 20))
label.attributedText = testAttributedString
self.view.addSubview(label)
Declare a Macro with your NSAttributedString.
Give that Attributed string to all textfields wherever you want.
So that when you change in Macro, will reflect in all places..
Check out the following.
let CommonTextFieldAttr : NSDictionary = [NSForegroundColorAttributeName:UIColor.redColor()]
And Use it in Textfield properties.
textField?.attributedPlaceholder = NSAttributedString(string: "sdfa",attributes: CommonTextFieldAttr as? [String : AnyObject])
Hope it helps..
I'm using attributed text (NSMutableAttributedString, to be exact) to display text that can be either italicized, bold, or have a hyperlink (sometimes combinations of these come up as well).
When I did this with UILabel it displayed exactly as I wanted it, but the links weren't functional. I switched over to UITextView and the links are fully functional, but the italicized and bold fonts aren't working.
We're pulling in the data from xml files, so I can't just specify certain words to set, but I don't think I would need to since this is working with labels.
"but the italicized and bold fonts aren't working"
Not sure what exactly is not working but here is a snippet of code that does change font traits in the attributed text:
#IBOutlet weak var content: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let fontDescriptor = UIFontDescriptor.preferredFontDescriptorWithTextStyle(UIFontTextStyleBody)
let existingTraitsWithNewTrait = fontDescriptor.symbolicTraits.rawValue | UIFontDescriptorSymbolicTraits.TraitBold.rawValue | UIFontDescriptorSymbolicTraits.TraitItalic.rawValue
let changedFontDescriptor: UIFontDescriptor = fontDescriptor.fontDescriptorWithSymbolicTraits(UIFontDescriptorSymbolicTraits(rawValue: existingTraitsWithNewTrait))!
let updatedFont = UIFont(descriptor: changedFontDescriptor, size:0.0)
let attributes = [NSFontAttributeName: updatedFont]
let attributedString: NSAttributedString? = NSAttributedString(string: "Hello World!", attributes: attributes)
content.attributedText = attributedString!
}