I'm trying to build a custom keyboard for iOS using images which I've put in as buttons. When I press a button, the image linked to the button is put into an attributed string which is loaded into an UiTextView inside the custom keyboard view. That is working.
The problem is that when I append a new image to the attributed string both the old and new images in the string are changing to the image I currently pressed on. I can't understand why the old images in the string are changing.
Any suggestions? I've tried using replaceCharactersInRange
and insertAttributedString
but can't get it to work. Here is the code (after viewDidLoad):
let textAttachment = NSTextAttachment()
let textView = UITextView(frame: CGRectMake(5, 5, 200, 40))
var attributedString = NSMutableAttributedString(string: "")
@IBAction func buttonPressed(button :UIButton) {
let string = button.titleLabel?.text
textAttachment.image = UIImage(named: "\(string!).png")!
textAttachment.image = UIImage(CGImage: textAttachment.image!.CGImage!, scale: 6, orientation: .Up)
let attrStringWithImage = NSAttributedString(attachment: textAttachment)
attributedString.appendAttributedString(attrStringWithImage);
textView.attributedText = attributedString;
}
From there you can drag a UIImageView and UILabel into the view you just created. Set the image of the UIImageView in the properties inspector as the custom bullet image (which you will have to add to your project by dragging it into the navigation pane) and you can write some text in the label.
The values for the attachment characteristics of attributed strings and related objects.
Attributed strings are character strings that have attributes for individual characters or ranges of characters. Attributes provide traits like visual styles for display, accessibility for guided access, and hyperlink data for linking between data sources.
We cannot simply append NSTextAttachment image .We have to store all the attached Image and concatenation to existing attributed String.
func buttonPressed(_ sender: Any) {
let button = sender as! UIButton
let tag = button.tag
let attributedString:NSAttributedString!
switch tag {
case 2:
attributedString = addAttributedText(text: (button.titleLabel?.text)!)
case 3:
attributedString = addAttributedText(text: (button.titleLabel?.text)!)
case 4:
attributedString = addAttributedText(text: (button.titleLabel?.text)!)
default:
attributedString = addAttributedText(text: "launch")
}
textView.attributedText = attributedString
}
func addAttributedText(text:String) -> NSAttributedString {
textViewDidChange(textView)
let axtractedImageAttribute = NSMutableAttributedString()
for image in imageArray {
let attachment:NSTextAttachment = NSTextAttachment()
attachment.image = image
attachment.setImageHeight(height: 20)
let attachmentString:NSAttributedString = NSAttributedString(attachment: attachment)
axtractedImageAttribute.append(attachmentString)
}
let attachment:NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: "\(text).png")
attachment.setImageHeight(height: 20)
let attachmentString:NSAttributedString = NSAttributedString(attachment: attachment)
let attributedString:NSMutableAttributedString = NSMutableAttributedString(string:textView.text!)
attributedString.append(axtractedImageAttribute)
attributedString.append(attachmentString)
return attributedString
}
//MARKS:- Extract attachedImage
func textViewDidChange(_ textView: UITextView) {
imageArray = [UIImage]()
let range = NSRange(location: 0, length: textView.attributedText.length)
if (textView.textStorage.containsAttachments(in: range)) {
let attrString = textView.attributedText
var location = 0
while location < range.length {
var r = NSRange()
let attrDictionary = attrString?.attributes(at: location, effectiveRange: &r)
if attrDictionary != nil {
let attachment = attrDictionary![NSAttachmentAttributeName] as? NSTextAttachment
if attachment != nil {
if attachment!.image != nil {
imageArray.append( attachment!.image!)
}
}
location += r.length
}
}
}
}
}
Demo Reference
This is my extension Swift 4, to change image to attributed String with size and tint color change
public extension UIImage {
func tint(with color: UIColor) -> UIImage {
var image = withRenderingMode(.alwaysTemplate)
UIGraphicsBeginImageContextWithOptions(size, false, scale)
color.set()
image.draw(in: CGRect(origin: .zero, size: size))
image = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
func toAttributedString(with heightRatio: CGFloat, tint color: UIColor? = nil) -> NSAttributedString {
let attachment = NSTextAttachment()
var image = self
if let tintColor = color {
image.withRenderingMode(.alwaysTemplate)
image = image.tint(with: tintColor)
}
attachment.image = image
let ratio: CGFloat = image.size.width / image.size.height
let attachmentBounds = attachment.bounds
attachment.bounds = CGRect(x: attachmentBounds.origin.x,
y: attachmentBounds.origin.y,
width: ratio * heightRatio,
height: heightRatio)
return NSAttributedString(attachment: attachment)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With