drawGlyphsForGlyphRange in Swift (Displaying Invisible characters). replaceGlyphAtIndex deprecated - swift

I am trying to show Invisible characters using below class & func, and it is working fine.
But replaceGlyphAtIndex is deprecated in OS X 10.11 and we need to use setGlyhs()
setGlyphs(<#T##glyphs: UnsafePointer<CGGlyph>##UnsafePointer<CGGlyph>#>, properties: <#T##UnsafePointer<NSGlyphProperty>#>, characterIndexes: <#T##UnsafePointer<Int>#>, font: <#T##NSFont#>, forGlyphRange: <#T##NSRange#>)
But i am having difficulties on how to convert replaceGlyphAtIndex to setGlyphs.
Can any one suggest me how to convert below replaceGlyphAtIndex to setGlyphs()?
I tried as below but it is crashing.
let data = String(g).dataUsingEncoding(NSUTF8StringEncoding)
let cgG = UnsafePointer<CGGlyph>(data!.bytes)
self.setGlyphs(cgG, properties: nil, characterIndexes: UnsafePointer<Int>(bitPattern: characterIndex), font: font as! NSFont, forGlyphRange: NSMakeRange(glyphIndex, 1))
Can any one let me know whats going wrong in the above line of code?
class MyLayoutManager: NSLayoutManager {
override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) {
if let storage = self.textStorage {
let s = storage.string
let startIndex = s.startIndex
for glyphIndex in glyphsToShow.location ..< glyphsToShow.location + glyphsToShow.length {
let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex)
let ch = s[startIndex.advancedBy(characterIndex)]
switch ch {
case " ": //Blank Space
let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
if let font = attrs[NSFontAttributeName] {
let g = font.glyphWithName("period")//("periodcentered")
self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
// let data = String(g).dataUsingEncoding(NSUTF8StringEncoding)
// let cgG = UnsafePointer<CGGlyph>(data!.bytes)
//
// self.setGlyphs(cgG, properties: nil, characterIndexes: UnsafePointer<Int>(bitPattern: characterIndex), font: font as! NSFont, forGlyphRange: NSMakeRange(glyphIndex, 1))
}
case "\n": //New Line
let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
if let font = attrs[NSFontAttributeName] {
let g = font.glyphWithName("logicalnot")
self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
}
case newLineUniCodeStr:
let attrs = storage.attributesAtIndex(characterIndex, effectiveRange: nil)
if let font = attrs[NSFontAttributeName] {
let g = font.glyphWithName("logicalnot")
self.replaceGlyphAtIndex(glyphIndex, withGlyph: g)
}
default:
break
}
}
}
super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin)
}
}

I found another way to display Glyphs. (Posting my code below as it may be useful to others)
override func drawGlyphsForGlyphRange(glyphsToShow: NSRange, atPoint origin: NSPoint) {
if let storage = self.textStorage {
let s = storage.string
let startIndex = s.startIndex
var padding:CGFloat = 0
for glyphIndex in glyphsToShow.location ..< glyphsToShow.location + glyphsToShow.length {
let characterIndex = self.characterIndexForGlyphAtIndex(glyphIndex)
if characterIndex < s.characters.count
{
var glyphStr = ""
let ch = s[startIndex.advancedBy(characterIndex)]
switch ch {
case " ": //Blank Space
glyphStr = periodCenteredUniCodeStr
case "\n": //New Line
glyphStr = lineBreakUniCodeStr
case newLineUniCodeStr:
glyphStr = lineBreakUniCodeStr
padding += 5
default:
break
}
var glyphPoint = self.locationForGlyphAtIndex(glyphIndex)
let glyphRect = self.lineFragmentRectForGlyphAtIndex(glyphIndex, effectiveRange: nil)
if glyphStr.characters.count > 0{
glyphPoint.x = glyphPoint.x + glyphRect.origin.x
glyphPoint.y = glyphRect.origin.y
NSString(string: glyphStr).drawInRect(NSMakeRect(glyphPoint.x, glyphPoint.y, 10, 10), withAttributes: nil)
}
}else{
print("Wrong count here")
}
}
}
super.drawGlyphsForGlyphRange(glyphsToShow, atPoint: origin)
}

Related

MapBox iOS: How to access a routes coordinates to draw it using polyline

I am trying to display a route using MGLPolylineFeature in Mapbox after having calculated it. The calculation works and my map resizes to the correct points, I just can't seem to display the Polyline because the way you get the coordinates seems to have changed. Here is my code
This calculates the route
func calculateRoute(from originCoor: CLLocationCoordinate2D, to destinationCoor: CLLocationCoordinate2D, completion: #escaping (Route?, Error?) -> Void ){
let origin = Waypoint(coordinate: originCoor, coordinateAccuracy: -1, name: "Start")
let destination = Waypoint(coordinate: destinationCoor, coordinateAccuracy: -1, name: "Finish")
let options = NavigationRouteOptions(waypoints: [origin, destination], profileIdentifier: .automobileAvoidingTraffic)
_ = Directions.shared.calculate(options, completionHandler: { (wayponts, result) in
switch result {
case .success(let response):
guard let route = response.routes?.first else { return }
self.directionsRoute = route
self.drawRoute(route: self.directionsRoute!)
let coordinateBounds = MGLCoordinateBounds(sw: destinationCoor, ne: originCoor)
let insets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
let routeCam = self.mapView.cameraThatFitsCoordinateBounds(coordinateBounds, edgePadding: insets)
//self.mapView.setDirection( 00.00 , animated: true)
self.mapView.setCamera(routeCam, animated: true)
case .failure(let error): print(error)
}
})
}
And this should be displaying it, but as I said, it does not work because route has no coordinates
func drawRoute(route: Route) {
//Schaut, ob route überhaupt koordinaten hat, um crashes zu vermeiden
guard route.coordinateCount > 0 else {return}
var routeCoordinates = route.coordinates!
let polyline = MGLPolylineFeature(coordinates: &routeCoordinates, count: route.coordinateCount)
//Create polyline
if let source = mapView.style?.source(withIdentifier: "route-source") as? MGLShapeSource {
source.shape = polyline
//Else
} else{
let source = MGLShapeSource(identifier: "route-source", features: [polyline], options: nil)
//Create Line Style to style it
let lineStyle = MGLLineStyleLayer(identifier: "route-style", source: source)
lineStyle.lineColor = MGLStyleConstantValue(rawValue: UIColor.blue)
lineStyle.lineWidth = MGLStyleConstantValue(rawValue: 4.0)
mapView.style?.addSource(source)
mapView.style?.addLayer(lineStyle)
}
}
The last bit with MGLStyleConstantValue seems to have issues also, but those ones are not as important. I should have all the correct imports and the correct pods.
Still, Xcode tells me "Value of type 'Route' has no member 'coordinates'". How do I access them? Or: How do I draw a Polyline without them?
func calculateRoute(from originCoor:CLLocationCoordinate2D, to destinationCoor:CLLocationCoordinate2D) {
let origin = Waypoint(coordinate: originCoor, coordinateAccuracy: -1, name: "Start")
let destination = Waypoint(coordinate: destinationCoor, coordinateAccuracy: -1, name: "End")
let options = NavigationRouteOptions(waypoints: [origin, destination], profileIdentifier: .automobileAvoidingTraffic)
_ = Directions.shared.calculate(options) { [weak self] (session, result) in
switch result {
case .failure(let error):
print(error.localizedDescription)
case .success(let response):
guard let route = response.routes?.first, let strongSelf = self else {
return
}
if let leg = route.legs.first {
for step in leg.steps {
if let coordinates = step.shape?.coordinates {
for (index, point) in coordinates.enumerated() {
let source = point
if index <= coordinates.count - 2 {
let destination = coordinates[index + 1]
strongSelf.getPolylineArray(source: source, destination: destination)
}
}
}
}
strongSelf.drowRoute()
}
let coordinateBounds = MGLCoordinateBounds(sw: destinationCoor, ne: originCoor)
let insets = UIEdgeInsets(top: 50, left: 50, bottom: 50, right: 50)
let routeCam = strongSelf.mapView.cameraThatFitsCoordinateBounds(coordinateBounds, edgePadding: insets)
strongSelf.mapView.setCamera(routeCam, animated: true)
}
}
}
private func getPolylineArray(source: CLLocationCoordinate2D, destination: CLLocationCoordinate2D){
addCordinatsArray(lat: source.latitude, lon: source.longitude)
addCordinatsArray(lat: destination.latitude, lon: destination.longitude)
}
func drowRoute() {
let polyline = MGLPolylineFeature(coordinates: allCoordinates, count: UInt(allCoordinates.count))
if let sourcce = mapView.style?.source(withIdentifier: "route-source") as? MGLShapeSource {
sourcce.shape = polyline
}else {
let source = MGLShapeSource(identifier: "route-source", features: [polyline], options: nil)
let lineStyle = MGLLineStyleLayer(identifier: "route-style", source: source)
lineStyle.lineJoin = NSExpression(forConstantValue: "round")
lineStyle.lineCap = NSExpression(forConstantValue: "round")
lineStyle.lineColor = NSExpression(forConstantValue: UIColor.gray)
// The line width should gradually increase based on the zoom level.
lineStyle.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %#)",
[14: 5, 18: 20])
mapView.style?.addSource(source)
mapView.style?.addLayer(lineStyle)
}
}
and added property
var allCoordinates: [CLLocationCoordinate2D] = []

Highlighting words with TextKit selecting the wrong occurrence of repeated words

I'm actually developing a Quran application in which I'm using TextKit to highlight verses and change their color. Everything is going great but I have a little problem with words that appear multiple times. First of all, my code is:
import UIKit
class ViewController: UIViewController {
let attributedBackgroundColor = [ NSBackgroundColorAttributeName: UIColor.lightGray ]
var myVerses = ["بِسْمِ اللَّهِ الرَّحْمَنِ الرَّحِيمِ","الْحَمْدُ لِلَّهِ رَبِّ الْعَالَمِينَ","الرَّحْمَنِ الرَّحِيمِ","مَالِكِ يَوْمِ الدِّينِ","إِيَّاكَ نَعْبُدُ وَإِيَّاكَ نَسْتَعِينُ","اهدِنَا الصِّرَاطَ الْمُسْتَقِيمَ","صِرَاطَ الَّذِينَ أَنْعَمْتَ عَلَيْهِمْ غَيْرِ الْمَغْضُوبِ عَلَيْهِمْ وَلاَ الضَّالِّينَ"]
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let string = NSMutableAttributedString(string: "Vide initialement")
string.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: CGFloat(25.0)), range: NSRange(location: 0, length: string.length))
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
string.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSRange(location: 0, length: string.length))
textView.attributedText = string
let singleTap = UITapGestureRecognizer(target: self, action: #selector(ViewController.tapRecognized))
singleTap.numberOfTapsRequired = 1
textView.addGestureRecognizer(singleTap)
textView.isEditable = false
textView.isSelectable = false
var str = ""
// Ajouter numérotation aux verses - Add numerotation to verses.
for i in 0..<myVerses.count {
if i > 0 {
str += "("+"\(i)"+")"
}
str += String(myVerses[i])
}
print(str)
textView.text = str + "("+"\(myVerses.count)"+")"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// Sélection des verses - Select Verses
func tapRecognized(_ recognizer: UITapGestureRecognizer) {
if recognizer.state == .recognized {
let point = recognizer.location(in: recognizer.view)
let detectedText = self.getWordAtPosition(pos: point, in: textView)
if (detectedText != "") {
print("detectedText == \(detectedText)")
let string = NSMutableAttributedString(string: self.textView.text)
let verses = self.textView.text.components(separatedBy: ")")
if let detectedRange = textView.text.range(of: detectedText) {
let startPosOfSubstring = textView.text.distance(from: textView.text.startIndex, to: detectedRange.lowerBound)
let detectedLength = detectedText.characters.count
let rangeOfSub = (startPosOfSubstring,detectedLength)
print("-- rangeofSub == " ,rangeOfSub)
let rangeOfSubstring = NSRange(location: startPosOfSubstring, length: detectedLength)
for verse: String in verses {
if let detectedVerse = textView.text.range(of: verse) {
let startPosOfVerse = textView.text.distance(from: textView.text.startIndex, to: detectedVerse.lowerBound)
let detectedLengthOfVerse = verse.characters.count
let tupleVerse = (startPosOfVerse,detectedLengthOfVerse)
print("++ rangeofVerse == " ,tupleVerse)
let rangeOfVerse = NSRange(location: startPosOfVerse, length: detectedLengthOfVerse)
print(verse)
let range = (self.textView.text as NSString).range(of: verse)
let contained = NSLocationInRange(rangeOfSubstring.location, rangeOfVerse)
if (contained) {
print ("************************************")
print("contained is :" ,contained)
print ("************************************")
string.addAttribute(NSForegroundColorAttributeName, value: UIColor.red, range: range)
string.addAttribute(NSBackgroundColorAttributeName, value: UIColor.darkGray, range: range)
string.addAttribute(NSFontAttributeName, value: UIFont.systemFont(ofSize: CGFloat(25.0)), range: NSRange(location: 0, length: string.length))
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .center
string.addAttribute(NSParagraphStyleAttributeName, value: paragraphStyle, range: NSRange(location: 0, length: string.length))
}
print ("--------------------------------------------")
}
}
self.textView.attributedText = string
} else {
print("detectedText is empty")
}
}
}
}
func getWordAtPosition( pos: CGPoint, in textview: UITextView) -> String {
//Eleminer le balancement du scroll - eliminate scroll offset
// var pos = pos
// pos.y += tv.contentOffset.y
//Position du text tapé au point - get location in text from textposition at point
let tapPos = textview.closestPosition(to: pos)
//Avoir le mot tapé dans la position du point - fetch the word at this position (or nil, if not available)
if let wr = textview.tokenizer.rangeEnclosingPosition(tapPos!, with: .word, inDirection: UITextLayoutDirection.right.rawValue) {
print(pos)
return textview.text(in: wr)!
}else{
return ""
}
}
}
When I run my app on the simulator, I select a verse and it's going great.
Normal Use:
The problem is with words that appear more than once. For example, for the word "الرحمن" which is found twice, regardless of whether I select the first or the second one, the verse that gets highlighted is always the first one.
What am I doing wrong?
Thanks in advance.
As I understand your question, you want to highlight the entire verse when the user selects a particular word in that verse.
The problem in your logic is that you determine the word that the user has selected, but that word is not necessarily unique, so when you search for this word (with textView.text.range(of: detectedText)), you get back the first result, which may or may not be the one the user actually selected.
One potential solution, if the tokenizer is correctly understanding your text, is to use a different TextGranularity when determining the user's selection. Instead of:
textview.tokenizer.rangeEnclosingPosition(tapPos!, with: .word, inDirection: UITextLayoutDirection.right.rawValue)
you could try:
textview.tokenizer.rangeEnclosingPosition(tapPos!, with: .sentence, inDirection: UITextLayoutDirection.right.rawValue)

NSAttributedString get images and string in parts

I have an NSAttributedString with a mixture of String and NSTextAttachment with images in there. How would I extract an [AnyObject] array of the parts?
This worked for me in Swift 4:
extension UITextView {
func getParts() -> [AnyObject] {
var parts = [AnyObject]()
let attributedString = self.attributedText
let range = NSMakeRange(0, attributedString.length)
attributedString.enumerateAttributes(in: range, options: NSAttributedString.EnumerationOptions(rawValue: 0)) { (object, range, stop) in
if object.keys.contains(NSAttributedStringKey.attachment) {
if let attachment = object[NSAttributedStringKey.attachment] as? NSTextAttachment {
if let image = attachment.image {
parts.append(image)
} else if let image = attachment.image(forBounds: attachment.bounds, textContainer: nil, characterIndex: range.location) {
parts.append(image)
}
}
} else {
let stringValue : String = attributedString.attributedSubstring(from: range).string
if (!stringValue.trimmingCharacters(in: .whitespaces).isEmpty) {
parts.append(stringValue as AnyObject)
}
}
}
return parts
}
I figured it out you can iterate over all the attributedString and read if the object has an NSTextAttachmentAttributeName property. If not, assume it's a string.
extension UITextView {
func getParts() -> [AnyObject] {
var parts = [AnyObject]()
let attributedString = self.attributedText
let range = NSMakeRange(0, attributedString.length)
attributedString.enumerateAttributesInRange(range, options: NSAttributedStringEnumerationOptions(rawValue: 0)) { (object, range, stop) in
if object.keys.contains(NSAttachmentAttributeName) {
if let attachment = object[NSAttachmentAttributeName] as? NSTextAttachment {
if let image = attachment.image {
parts.append(image)
}else if let image = attachment.imageForBounds(attachment.bounds, textContainer: nil, characterIndex: range.location) {
parts.append(image)
}
}
}else {
let stringValue : String = attributedString.attributedSubstringFromRange(range).string
if !stringValue.isEmptyOrWhitespace() {
parts.append(stringValue)
}
}
}
return parts
}
}

Get index of string in an array of NSAttributedString in swift

I have an array of NSattributedstring. I want to get the index of a string in the array. I am using the following code.
let textToSearch = "My name is Amrit"
let index = find(myArray, textToSearch)
myArray is an array of NsattributedString. As both the types of array and textTosearch are different.
indexArray = ["1. THE OPENING","2. THE COW","3. THE FAMILY OF IMRAN","4. WOMEN","5. THE FEAST","6. LIVESTOCK","7. THE HEIGHTS","8. BATTLE GAINS","9. REPENTANCE","10. JONAH","11. HUD","12. JOSEPH","13. THUNDER","14. ABRAHAM","15. AL-HIJR","16. THE BEE","17. THE NIGHT JOURNEY","18. THE CAVE","19. MARY","20. TA HA","21. THE PROPHETS","22. THE PILGRIMAGE","23. THE BELIEVERS","24. LIGHT","25. THE DIFFERENTIATOR","26. THE POETS","27. THE ANTS","28. THE STORY","29. THE SPIDER","30. THE BYZANTINES","31. LUQMAN","32. BOWING DOWN IN WORSHIP","33. THE JOINT FORCES","34. SHEBA","35. THE CREATOR","36. YA SIN","37. RANGED IN ROWS","38. SAD","39. THE THRONGS","40. THE FORGIVER","41. [VERSES] MADE DISTINCT","42. CONSULTATION","43. ORNAMENTS OF GOLD","44. SMOKE","45. KNEELING","46. THE SAND DUNES","47. MUHAMMAD","48. TRIUMPH","49. THE PRIVATE ROOMS","50. QAF","51. SCATTERING [WINDS]","52. THE MOUNTAIN","53. THE STAR","54. THE MOON","55. THE LORD OF MERCY","56. THAT WHICH IS COMING","57. IRON","58. THE DISPUTE","59. THE GATHERING [OF FORCES]","60. WOMEN TESTED","61. SOLID LINES","62. THE DAY OF CONGREGATION","63. THE HYPOCRITES","64. MUTUAL NEGLECT","65. DIVORCE","66. PROHIBITION","67. CONTROL","68. THE PEN","69. THE INEVITABLE HOUR","70. THE WAYS OF ASCENT","71. NOAH","72. THE JINN","73. ENFOLDED","74. WRAPPED IN HIS CLOAK","75. THE RESURRECTION","76. MAN","77. [WINDS] SENT FORTH","78. THEANNOUNCEMENT","79. THE FORCEFUL CHARGERS","80. HE FROWNED","81. SHROUDED IN DARKNESS","82. TORN APART","83. THOSE WHO GIVE SHORT MEASURE","84. RIPPED APART","85. THE TOWERING CONSTELLATIONS","86. THE NIGHT-COMER","87. THE MOST HIGH","88. THE OVERWHELMING EVENT","89. DAYBREAK","90. THE CITY","91. THE SUN","92. THE NIGHT","93. THE MORNING BRIGHTNESS","94. RELIEF","95. THE FIG","96. THE CLINGING FORM","97. THE NIGHT OF GLORY","98. CLEAR EVIDENCE","99. THE EARTHQUAKE","100. THE CHARGING STEEDS","101. THE CRASHING BLOW","102. STRIVING FOR MORE","103. THE DECLINING DAY","104. THE BACKBITER","105. THE ELEPHANT","106. QURAYSH","107. COMMON KINDNESSES","108. ABUNDANCE","109. THE DISBELIEVERS","110. HELP","111. PALM FIBRE","112. PURITY [OF FAITH]","113. DAYBREAK","114. PEOPLE"]
if let rtfPath = NSBundle.mainBundle().URLForResource("quaran3", withExtension: "rtf") {
let attributedStringWithRtf = NSMutableAttributedString(fileURL: rtfPath, options: [NSDocumentTypeDocumentAttribute:NSRTFTextDocumentType], documentAttributes: nil, error: nil)!
var lengthOfRtf = attributedStringWithRtf.length
divideFactor = Int(lengthOfRtf/endIndex)
println(divideFactor)
self.updateTextFont(attributedStringWithRtf) (valueFactor: divideFactor) (totalRange: lengthOfRtf)
self.getTheIndexNumber(attributedStringWithRtf) (valueFactor: divideFactor)
}
func updateTextFont(mystring:NSMutableAttributedString) (valueFactor:Int) (totalRange : Int) {
let screenSizeMain: CGRect = UIScreen.mainScreen().bounds
let myAttriText:NSMutableAttributedString = mystring.mutableCopy() as! NSMutableAttributedString
myAttriText.beginEditing()
myAttriText.enumerateAttributesInRange(NSMakeRange(0, myAttriText.length), options: NSAttributedStringEnumerationOptions.Reverse) { (attribute, range, stop) -> Void in
var mutableAttributes = NSDictionary(dictionary: attribute)
var font:UIFont = mutableAttributes.objectForKey(NSFontAttributeName) as! UIFont
var newFont:UIFont = font.fontWithSize(font.pointSize)
if DeviceType.IS_IPAD
{
newFont = font.fontWithSize(font.pointSize+7)
}
var fontProperties:UIFontDescriptor = font.fontDescriptor()
let sizeNumber:Float = fontProperties.fontAttributes()[UIFontDescriptorSizeAttribute] as! Float
myAttriText.addAttribute(NSFontAttributeName, value: newFont, range: range)
}
for var i=0; i <= valueFactor; i++ {
// if valueFactor*
if startIndex == 885819 {
}
//let checkCurrentRange = valueFactor*endIndex
let exactEndIndex : Int = endIndex
if (totalRange - startIndex) < endIndex
{
endIndex = totalRange - startIndex
let rangeFinal = NSMakeRange(startIndex, endIndex)
var nsTextFinal = myAttriText.attributedSubstringFromRange(rangeFinal)
pageText.append(nsTextFinal)
endIndex = exactEndIndex
return
}
let range = NSMakeRange(startIndex, endIndex)
var nsText = myAttriText.attributedSubstringFromRange(range)
for index in endIndex..<endIndex+100
{
let rangeNew = NSMakeRange(endIndex + startIndex-1,1)
var checkSpace = myAttriText.attributedSubstringFromRange(rangeNew)
let stirngNew = checkSpace.string
if stirngNew == " "
{
break;
}
else
{
endIndex++
}
}
let rangeFinal = NSMakeRange(startIndex, endIndex)
var nsTextFinal = myAttriText.attributedSubstringFromRange(rangeFinal)
pageText.append(nsTextFinal)
println(startIndex)
startIndex = startIndex + endIndex
endIndex = exactEndIndex
}
myAttriText.endEditing()
}
func visibleRangeOfTextView(textView: NSAttributedString) -> NSRange {
return NSMakeRange(startIndex, endIndex)
}
func getTheIndexNumber(attribSring:NSMutableAttributedString) (valueFactor:Int) //-> NSMutableArray
{
for index in 0..<indexArray.count {
let text = indexArray.objectAtIndex(index) as! NSString
var lengthOfRtf = attribSring.length
var textNew = attribSring.attributedSubstringFromRange(NSMakeRange(0, lengthOfRtf))
var stirngNew : NSMutableString = attribSring.mutableString.mutableCopy() as! NSMutableString
// println(stirngNew)
var range: NSRange = (stirngNew as NSString).rangeOfString(text as String)
var checkSpace = attribSring.attributedSubstringFromRange(range)
let stirngNewCheck = checkSpace.string
let pageIndex = (range.location / endIndex)
indeArryInt.addObject(pageIndex)
}
}
You can try this:
let index = myArray.indexOf { $0.string == textToSearch }
This returns an Int?, the first index where a match is found. If there's no match, it will return nil.
If you are using Swift 1.2 you will need to map array of NSAttributedString to String array so find function can be called with correct values
let index = find(myArray.map({ attributedString in
return attributedString.string
}), textToSearch)
#ZoffDino answer would work like a charm in Swift 2.0

How do I insert an image Inline UILabel in iOS 8 using swift

I followed the following post on how to use NSTextAttachment to add images inline with your UILabels. I followed the best I could and wrote my version in Swift.
I am creating a chat application and the field that I'm inserting a beer icon into does not render the image or doesn't seem to render the image inline. I don't get any errors so I'm assuming I'm missing some small little detail in my code.
var beerName:String!
if(sender == bn_beer1)
{
beerName = "beer1.png"
}
if(sender == bn_beer2)
{
beerName = "beer2.png"
}
if(sender == bn_beer3)
{
beerName = "beer3"
}
var attachment:NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: beerName)
var attachmentString:NSAttributedString = NSAttributedString(attachment: attachment)
var myString:NSMutableAttributedString = NSMutableAttributedString(string: inputField.text)
myString.appendAttributedString(attachmentString)
inputField.attributedText = myString;
This does not work on a UITextField. It only works on a UILabel.
Here is a UILabel extension based on your code (Swift 2.0)
extension UILabel
{
func addImage(imageName: String)
{
let attachment:NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: imageName)
let attachmentString:NSAttributedString = NSAttributedString(attachment: attachment)
let myString:NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
myString.appendAttributedString(attachmentString)
self.attributedText = myString
}
}
EDIT:
here is a new version that allow to add the icon before or after the label. There is also a function to remove the icon from the label
extension UILabel
{
func addImage(imageName: String, afterLabel bolAfterLabel: Bool = false)
{
let attachment: NSTextAttachment = NSTextAttachment()
attachment.image = UIImage(named: imageName)
let attachmentString: NSAttributedString = NSAttributedString(attachment: attachment)
if (bolAfterLabel)
{
let strLabelText: NSMutableAttributedString = NSMutableAttributedString(string: self.text!)
strLabelText.appendAttributedString(attachmentString)
self.attributedText = strLabelText
}
else
{
let strLabelText: NSAttributedString = NSAttributedString(string: self.text!)
let mutableAttachmentString: NSMutableAttributedString = NSMutableAttributedString(attributedString: attachmentString)
mutableAttachmentString.appendAttributedString(strLabelText)
self.attributedText = mutableAttachmentString
}
}
func removeImage()
{
let text = self.text
self.attributedText = nil
self.text = text
}
}
Regis St-Gelais's extension answer for Swift 3 and Swift 4 and without forced unwrapping:
extension UILabel {
func addImageWith(name: String, behindText: Bool) {
let attachment = NSTextAttachment()
attachment.image = UIImage(named: name)
let attachmentString = NSAttributedString(attachment: attachment)
guard let txt = self.text else {
return
}
if behindText {
let strLabelText = NSMutableAttributedString(string: txt)
strLabelText.append(attachmentString)
self.attributedText = strLabelText
} else {
let strLabelText = NSAttributedString(string: txt)
let mutableAttachmentString = NSMutableAttributedString(attributedString: attachmentString)
mutableAttachmentString.append(strLabelText)
self.attributedText = mutableAttachmentString
}
}
func removeImage() {
let text = self.text
self.attributedText = nil
self.text = text
}
}
Usage:
self.theLabel.text = "desiredText"
self.theLabel.addImageWith(name: "nameOfImage", behindText: false)
Edit 19/03/18 : Correct bug when imageBehindText = false + Image size in pixel no point.
David's function update for multiple images with text saving and image size based on font size (Swift 4) :
extension UILabel {
/**
This function adding image with text on label.
- parameter text: The text to add
- parameter image: The image to add
- parameter imageBehindText: A boolean value that indicate if the imaga is behind text or not
- parameter keepPreviousText: A boolean value that indicate if the function keep the actual text or not
*/
func addTextWithImage(text: String, image: UIImage, imageBehindText: Bool, keepPreviousText: Bool) {
let lAttachment = NSTextAttachment()
lAttachment.image = image
// 1pt = 1.32px
let lFontSize = round(self.font.pointSize * 1.32)
let lRatio = image.size.width / image.size.height
lAttachment.bounds = CGRect(x: 0, y: ((self.font.capHeight - lFontSize) / 2).rounded(), width: lRatio * lFontSize, height: lFontSize)
let lAttachmentString = NSAttributedString(attachment: lAttachment)
if imageBehindText {
let lStrLabelText: NSMutableAttributedString
if keepPreviousText, let lCurrentAttributedString = self.attributedText {
lStrLabelText = NSMutableAttributedString(attributedString: lCurrentAttributedString)
lStrLabelText.append(NSMutableAttributedString(string: text))
} else {
lStrLabelText = NSMutableAttributedString(string: text)
}
lStrLabelText.append(lAttachmentString)
self.attributedText = lStrLabelText
} else {
let lStrLabelText: NSMutableAttributedString
if keepPreviousText, let lCurrentAttributedString = self.attributedText {
lStrLabelText = NSMutableAttributedString(attributedString: lCurrentAttributedString)
lStrLabelText.append(NSMutableAttributedString(attributedString: lAttachmentString))
lStrLabelText.append(NSMutableAttributedString(string: text))
} else {
lStrLabelText = NSMutableAttributedString(attributedString: lAttachmentString)
lStrLabelText.append(NSMutableAttributedString(string: text))
}
self.attributedText = lStrLabelText
}
}
func removeImage() {
let text = self.text
self.attributedText = nil
self.text = text
}
}
Use these below extension it will work for sure
extension NSMutableAttributedString {
static func addImageInBetweenString(firstSentence: String, image: UIImage?, lastSentence: String) -> NSMutableAttributedString {
// create an NSMutableAttributedString that we'll append everything to
let fullString = NSMutableAttributedString(string: firstSentence)
// create our NSTextAttachment
let image1Attachment = NSTextAttachment()
image1Attachment.image = image
//image1Attachment.setImageHeight(height: 15.0)
Image1Attachment.bounds = CGRect(x: 0, y: -5, width: 15, height: 15)
// wrap the attachment in its own attributed string so we can append it
let image1String = NSAttributedString(attachment: image1Attachment)
// add the NSTextAttachment wrapper to our full string, then add some more text.
fullString.append(image1String)
fullString.append(NSAttributedString(string: lastSentence))
return fullString
}
}
extension NSTextAttachment {
func setImageHeight(height: CGFloat) {
guard let image = image else { return }
let ratio = image.size.width / image.size.height
bounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: ratio * height, height: height)
}
}