Swift - iOS Charts - Two LineChart views using Two datasets in one ViewController - swift

I have an app where I'm getting some data from Firebase by days and I'm trying to display each day's data in a separate chart, using iOS Charts, but it crashes with an error 'fatal error: Index out of range'. Below is my code:
func setChart(dataPoints: [String], values: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = ChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let lineChartDataSet = LineChartDataSet(yVals: dataEntries, label: "Units Sold")
let lineChartData = LineChartData(xVals: dataPoints, dataSet: lineChartDataSet)
firstDayChart.data = lineChartData
}
func setChart2(dataPoints: [String], values: [Double]) {
var dataEntries: [ChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = ChartDataEntry(value: values[i], xIndex: i)
dataEntries.append(dataEntry)
}
let lineChartDataSet = LineChartDataSet(yVals: dataEntries, label: "Units Sold")
let lineChartData = LineChartData(xVals: dataPoints, dataSet: lineChartDataSet)
secondDayChart.data = lineChartData
}
//BUTTON
#IBAction func getResults(sender: UIButton) {
let doubleArray = BGdataOutsideDay1.map { Double($0)!}
// print(doubleArray)
// var newArray:[Double] = []
self.doublesArrayDay1 = doubleArray
if doublesArrayDay1.count != 0 {
let bgAvg = doubleArray.reduce(0, combine: +) / Double(doubleArray.count)
let highest = doubleArray.maxElement()
let lowest = doubleArray.minElement()
self.bgHighestDay1 = String(format: "%.1f", highest!)
self.bgAvgDay1 = String(format: "%.1f", bgAvg)
self.bgLowestDay1 = String(format: "%.1f", lowest!)
self.firstDayHighest.text = self.bgHighestDay1! + " " + "\(self.units!)"
self.firstDayAvg.text = self.bgAvgDay1! + " " + "\(self.units!)"
self.firstDayLowest.text = self.bgLowestDay1! + " " + "\(self.units!)"
} else {
self.firstDayHighest.text = "0" + " " + "\(self.units!)"
self.firstDayAvg.text = "0" + " " + "\(self.units!)"
self.firstDayLowest.text = "0" + " " + "\(self.units!)"
}
let doubleArray2 = BGdataOutsideDay2.map { Double($0)!}
// print(doubleArray)
// var newArray:[Double] = []
self.doublesArrayDay2 = doubleArray
if doublesArrayDay2.count != 0 {
let bgAvg2 = doubleArray2.reduce(0, combine: +) / Double(doubleArray.count)
let highest2 = doubleArray2.maxElement()
let lowest2 = doubleArray2.minElement()
self.bgHighestDay2 = String(format: "%.1f", highest2!)
self.bgAvgDay2 = String(format: "%.1f", bgAvg2)
self.bgLowestDay2 = String(format: "%.1f", lowest2!)
self.secondDayHighest.text = self.bgHighestDay2! + " " + "\(self.units!)"
self.secondDayAvg.text = self.bgAvgDay2! + " " + "\(self.units!)"
self.secondDayLowest.text = self.bgLowestDay2! + " " + "\(self.units!)"
} else {
self.secondDayHighest.text = "0" + " " + "\(self.units!)"
self.secondDayAvg.text = "0" + " " + "\(self.units!)"
self.secondDayLowest.text = "0" + " " + "\(self.units!)"
setChart(DateOutSideDay1, values: doublesArrayDay1)
setChart(DateOutSideDay2, values: doublesArrayDay2)
}
Any help would be greatly appreciated! Thanks in advance! :)

First of all i think it's weird that you have a set of data points and another set of values, but they aren't the same size? Imo those sets should be the same size since the values represent the data points.
Anywho..
You can fix this issue by replacing let dataEntry = ChartDataEntry(value: values[i], xIndex: i) with let dataEntry = ChartDataEntry(value: i < values.count ? values[i] : 0, xIndex: i)
You can define a default value to be used when there's no value available at that index in the values array, I set it to 0.

Related

Own "Circle The Dot" Game - weird behavior

First things first: This probably isn't a quick-to-answer-question :(
If you have enough time - read on.
My little project
I want to recreate the Circle The Dot Game by Ketchapp in less than 500 Lines of code. You can try a replica online here. I implemented this using a 2D-Array of UIButtons. Here you can take a quick look of how it looks (the labels are for debugging)
Screenshot of the game scene. I use this GitHub project for orientation (its Java Code).
Complete code
You can just download the whole project: DOWNLOAD PROJECT
Or copy the code:
(just added a view called dotView in the Main.storyboard with measures: X=20, Y=296, Width = 394, Height 390.) Thats all:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var dotView: UIView!
var rowBtnArr: [UIButton] = [] // Simple 1D-Array of ALL Buttons
var btnArr: [[UIButton]] = [] // 2D-Array with ALL Buttons
let defaultColor = UIColor.lightGray
let playerColor = UIColor.systemBlue
let objColor = UIColor.orange
var button: UIButton = UIButton()
var playerX = 4
var playerY = 4
override func viewDidLoad() {
super.viewDidLoad()
buildBoard()
btnArr = Utils.arrayToArrays(arr: rowBtnArr, size: 9)
for btnline in btnArr{
for btn in btnline{
dotView.addSubview(btn)
}
}
btnArr[playerX][playerY].backgroundColor = playerColor
updateTitleColors()
}
#objc func buttonAction(sender: UIButton!) {
print("Button " + sender.title(for: .normal)! + " tapped")
if(sender.backgroundColor == defaultColor){
btnArr[getX(btn: sender)][getY(btn: sender)].backgroundColor = objColor
btnArr[playerX][playerY].backgroundColor = defaultColor
oneStep()
}
//btnArr[playerX][playerY].backgroundColor = defaultColor
//btnArr[playerX][playerY].backgroundColor = playerColor
updateTitleColors()
}
func setupBtn( x: Int, y: Int)->UIButton{
let btnSize = 40
if(y%2==0){ //Gerade Zeilen normal
button = UIButton(frame: CGRect(x: x*btnSize, y: y*btnSize, width: btnSize-3, height: btnSize-3))
}else{ // Ungerade Zeilen versetzt
button = UIButton(frame: CGRect(x: x*btnSize+20, y: y*btnSize, width: btnSize-3, height: btnSize-3))
}
button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.clipsToBounds = true
button.backgroundColor = defaultColor
button.setTitle(String(x) + " " + String(y), for: .normal)
button.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
//button.setTitleColor(button.backgroundColor, for: .normal)
return button
}
func buildBoard(){
for i in 0...8{
for j in 0...8{
rowBtnArr.append(setupBtn(x: i, y: j))
}
}
}
func getXFromString(string: String) -> Int{ //"2 4"
let separator = " "
let newstr = string
guard let x = newstr.components(separatedBy: separator).first else { return 99 }
return Int(x)!
}
func getYFromString(string: String) -> Int{ //"2 4"
let separator = " "
let newstr = string
guard let x = newstr.components(separatedBy: separator).last else { return 99 }
return Int(x)!
}
func getX(btn: UIButton) -> Int{
let separator = " "
let newstr = btn.title(for: .normal)
guard let x = newstr?.components(separatedBy: separator).first else { return 99 }
return Int(x)!
}
func getY(btn: UIButton) -> Int{
let separator = " "
let newstr = btn.title(for: .normal)
guard let y = newstr?.components(separatedBy: separator).last else { return 99 }
return Int(y)!
}
func updateTitleColors(){
// for btnline in btnArr{
// for btn in btnline{
// btn.setTitleColor(btn.backgroundColor, for: .normal)
// }
// }
}
func oneStep(){
let point = String(playerX) + " " + String(playerY)
if(isOnBorder(point: point)){
playerX = -1
playerY = -1
// YOU LOST ENDSCREEN
print("YOU LOST")
reset()
}else{
let direction = findDirection()
if(getXFromString(string: direction) == -1){
// YOU WON
print("YOU WON")
reset()
}else{
playerX = getXFromString(string: direction)
playerY = getYFromString(string: direction)
btnArr[playerX][playerY].backgroundColor = playerColor
}
}
}
func findDirection()->String{
var blocked: [String] = [] //Array enthält z.B. "3 5", "5 5"
let myQueue = LinkedQueue<Pair>()
let pair = Pair()
var possibleNeighbours = findPossibleNeighbours(btn: btnArr[playerX][playerY], blockedArr: blocked)
print(String(possibleNeighbours.description) + " possibeNeighs beginning" )
possibleNeighbours.shuffle()
for neighbour in possibleNeighbours{
if(isOnBorder(point: neighbour)){
print("Is on border")
return neighbour
}
pair.setPair(firstValue: neighbour, secondValue: neighbour)
myQueue.enqueue(value: pair)
blocked.append(neighbour)
}
while(!myQueue.isEmpty){
print("SCHLEIFE")
print((myQueue.tail?.value.first)! + " " + (myQueue.tail?.value.second)!)
let pointPair = myQueue.dequeue()
possibleNeighbours = findPossibleNeighbours(btn: btnArr[getXFromString(string: (pointPair?.value.first)!)][getYFromString(string: (pointPair?.value.first)!)], blockedArr: blocked)
for neighbour in possibleNeighbours{
if isOnBorder(point: neighbour){
return (pointPair?.value.getSecond())!
}
pair.setPair(firstValue: neighbour, secondValue: (pointPair?.value.getSecond())!)
myQueue.enqueue(value: pair)
blocked.append(neighbour)
}
}
return "-1 -1"
}
func isOnBorder(point: String)->Bool{
return (getXFromString(string: point) == 0 || getXFromString(string: point) == 8 || getYFromString(string: point) == 0 || getYFromString(string: point) == 8)
}
func findPossibleNeighbours(btn: UIButton,blockedArr: [String])->[String]{
var neighbours: [String] = []
let x = getX(btn: btn)
let y = getY(btn: btn)
if(y%2==1){
if(isFree(btn: btnArr[x][y-1])){ //OBEN
let a = String(x) + " " + String(y-1)
if(!blockedArr.contains(a)){
neighbours.append(a)
}
}
if(isFree(btn: btnArr[x+1][y-1])){ //OBENRECHTS
let b = String(x+1) + " " + String(y-1)
if(!blockedArr.contains(b)){
neighbours.append(b)
}
}
if(isFree(btn: btnArr[x+1][y])){ //RECHTS
let c = String(x+1) + " " + String(y)
if(!blockedArr.contains(c)){
neighbours.append(c)
}
}
if(isFree(btn: btnArr[x+1][y+1])){ //UNTENRECHTS
let d = String(x+1) + " " + String(y+1)
if(!blockedArr.contains(d)){
neighbours.append(d)
}
}
if(isFree(btn: btnArr[x][y+1])){ //UNTEN
let e = String(x) + " " + String(y+1)
if(!blockedArr.contains(e)){
neighbours.append(e)
}
}
if(isFree(btn: btnArr[x-1][y])){ //LINKS
let f = String(x-1) + " " + String(y)
if(!blockedArr.contains(f)){
neighbours.append(f)
}
}
}else{
if(isFree(btn: btnArr[x][y-1])){ //OBEN
let aa = String(x) + " " + String(y-1)
if(!blockedArr.contains(aa)){
neighbours.append(aa)
}
}
if(isFree(btn: btnArr[x+1][y])){ //RECHTS
let bb = String(x+1) + " " + String(y)
if(!blockedArr.contains(bb)){
neighbours.append(bb)
}
}
if(isFree(btn: btnArr[x][y+1])){ //UNTEN
let cc = String(x) + " " + String(y+1)
if(!blockedArr.contains(cc)){
neighbours.append(cc)
}
}
if(isFree(btn: btnArr[x-1][y+1])){ //UNTENLINKS
let dd = String(x-1) + " " + String(y+1)
if(!blockedArr.contains(dd)){
neighbours.append(dd)
}
}
if(isFree(btn: btnArr[x-1][y])){ //LINKS
let ee = String(x-1) + " " + String(y)
if(!blockedArr.contains(ee)){
neighbours.append(ee)
}
}
if(isFree(btn: btnArr[x-1][y-1])){ //OBENLINKS
let ff = String(x-1) + " " + String(y-1)
if(!blockedArr.contains(ff)){
neighbours.append(ff)
}
}
}
return neighbours
}
func isFree(btn: UIButton)->Bool{
if(btn.backgroundColor == defaultColor){
return true
}
return false
}
func reset(){
playerX = 4
playerY = 4
for btnline in btnArr{
for btn in btnline{
btn.backgroundColor = defaultColor
}
}
btnArr[4][4].backgroundColor = playerColor
}
}
// (0,0) (1,0) (2,0) (3,0) (4,0) (5,0) (6,0) (7,0) (8,0)
// (0,1) (1,1) (2,1) (3,1) (4,1) (5,1) (6,1) (7,1) (8,1)
// (0,2) (1,2) (2,2) (3,2) (4,2) (5,2) (6,2) (7,2) (8,2)
// (0,3) (1,3) (2,3) (3,3) (4,3) (5,3) (6,3) (7,3) (8,3)
// (0,4) (1,4) (2,4) (3,4) (4,4) (5,4) (6,4) (7,4) (8,4)
// (0,5) (1,5) (2,5) (3,5) (4,5) (5,5) (6,5) (7,5) (8,5)
// (0,6) (1,6) (2,6) (3,6) (4,6) (5,6) (6,6) (7,6) (8,6)
// (0,7) (1,7) (2,7) (3,7) (4,7) (5,7) (6,7) (7,7) (8,7)
// (0,8) (1,8) (2,8) (3,8) (4,8) (5,8) (6,8) (7,8) (8,8)
// _____________________________________________________
// row1 row2 row3 row4 row5 row6 row7 row8 row9
// for btnline in btnArr{
// print("-----------")
// for btn in btnline{
// print(btn.title(for: .normal)!)
// }
// }
//____________________________________________________
class Node<T> {
var value:T
var next:Node?
init(value:T) {
self.value = value
}
}
class LinkedQueue<T> {
var tail:Node<T>?
var head:Node<T>?
var count:Int = 0
var isEmpty: Bool{
return (count == 0)
}
func dequeue () -> Node<T>? {
if let node = head {
head = head?.next
count -= 1
return node
}
return nil
}
func enqueue(value:T) {
let newNode = Node(value:value)
if let tailNode = tail {
tailNode.next = newNode
newNode.next = nil
tail = newNode
} else {
head = newNode
tail = newNode
}
count += 1
}
}
import Foundation
class Utils{
static func arrayToArrays<T>(arr: Array<T>,size: Int)->Array<Array<T>>{
var result : Array<Array<T>> = Array<Array<T>>(repeating: Array<T>(), count: size);
var set = -1;
let expectedSize = (arr.count / size) + (arr.count % size);
for i in 0..<arr.count {
if i % expectedSize == 0{
set+=1;
}
result[set].append(arr[i]);
}
return result;
}
}
import Foundation
class Pair{
var first: String = ""
var second: String = ""
func setPair(firstValue: String, secondValue: String){
first = firstValue
second = secondValue
}
func getFirst() -> String{
return first
}
func getSecond() -> String{
return second
}
}
My Problem
When I play it without removing the trail (Button stays orange when visited) it ALMOST works perfectly, you can test this by uncommenting line 41. But with removing the trail (Like original Circle the dot) it is very confusing. I tried to find the problem for multiple hours now - thats why I post everything here :/
When I click button (2,3) at the very beginning it stops working - found no way to the border, but I only blocked ONE button... I just makes no sense when you compare it to the findDirection method, which includes the Breadth-First-Search-Algorithm. Same thing with some other buttons.
Its a little better when uncommenting line 135, which adds random to the game.
It would be sooo awesome if someone can help me or join the project. Its just for fun, I want to learn new things.
Greetings
SwiftHobby

Different results between xcode and simulator

in the picture a part of screenshot with a xcode printed code and the same result on an iPad simulator, can any one tell me why please?
here is the code
func loadLevel() {
var clueString = ""
var solutionString = ""
var letterBits = [String]()
if let levelFileURL = Bundle.main.url(forResource: "level\(level)", withExtension: ".txt") {
if let levelContents = try? String(contentsOf: levelFileURL) {
var lines = levelContents.components(separatedBy: "\n")
lines.shuffle()
for (index, line) in lines.enumerated() {
let parts = line.components(separatedBy: ": ")
let answer = parts[0]
let clue = parts[1]
clueString += "\(index + 1). \(clue)\n"
let solutionWord = answer.replacingOccurrences(of: "|", with: "")
solutionString += "\(solutionWord.count) letters\n"
solutions.append(solutionWord)
let bits = answer.components(separatedBy: "|")
letterBits += bits
}
}
}
print(clueString)
cluesLable.text = clueString.trimmingCharacters(in: .whitespacesAndNewlines)
answerLable.text = solutionString.trimmingCharacters(in: .whitespacesAndNewlines)
letterButtons.shuffle()
if letterButtons.count == letterBits.count {
for i in 0..<letterButtons.count {
letterButtons[i].setTitle(letterBits[i], for: .normal)
}
}
}
you need to remove labels line limitation in your code or your storyboard.
in code:
#IBOutlet weak var descriptionLabel: UILabel!{
didSet {
self.descriptionLabel.numberOfLines = 0
}
}

UIImage won't take anything but the array

I can't get the UIImage to take anything except the array. I have tried several different options including creating a var or putting the image name in there and I can't seem to get it to take.
#IBAction func displayPressed(_ sender: Any) {
externalPicture.image = UIImage (named: externalArray [1])
seatingSetup.image = UIImage (named: seatingArray ["\(forPicture())" + "seating"])
}
func forPicture() -> String {
var aircraftFullName = ""
let manuf = aircraftType.selectedRow(inComponent: 0)
let model = aircraftType.selectedRow(inComponent: 1)
if manuf < aircraft.count {
aircraftFullName = aircraft[manuf]
if let aircraftModels = models[aircraftFullName] {
if model < aircraftModels.count {
aircraftFullName = aircraftFullName + " " + aircraftModels[model]
}
}
}
selectedAircraft.text = aircraftFullName
if aircraftFullName != "0" {}
return "\(aircraftFullName)"
}
let aircraft = ["Airbus", "Boeing", "Bombardier", "Embraer"]
var models = [ "Airbus": ["A300", "A320", "A330", "A340", "A350", "A380"], "Boeing": ["B737", "B747", "B757", "767", "777", "787"], "Bombardier": ["CRJ200", "CRJ700/900"], "Embraer": ["ERJ145", "ERJ170", "ERJ190"] ]
var externalArray = ["320outside", "747outside"]
var seatingArray = ["AirbusA320seating", "BoeingB747seating"]
You can try to convert this
aircraftFullName = aircraftFullName + " " + aircraftModels[model]
to
aircraftFullName = aircraftFullName + aircraftModels[model]
Edit : according to your forPicture() method the image should be like this Airbus A320seating but for AirbusA320seating you need to remove the mentioned space like above
seatingSetup.image = UIImage (named:"\(forPicture())" + "seating")
//
if you want to keep space to show in the textfield you can create other var and return it like this
func forPicture() -> String {
var aircraftFullName = ""
var aircraftFullNameNoSpace = ""
let manuf = aircraftType.selectedRow(inComponent: 0)
let model = aircraftType.selectedRow(inComponent: 1)
if manuf < aircraft.count {
aircraftFullName = aircraft[manuf]
if let aircraftModels = models[aircraftFullName] {
if model < aircraftModels.count {
aircraftFullName = aircraftFullName + " " + aircraftModels[model]
aircraftFullNameNoSpace = aircraftFullName + aircraftModels[model]
}
}
}
selectedAircraft.text = aircraftFullName
if aircraftFullName != "0" {}
return aircraftFullNameNoSpace
}

Formatting Numbers in Swift 3

I want to format number to this: 123.234.234.234 from 123234234234 depends on what the user types into the text field.
I don't want to manage currency, it's not about currency, it is about the user has to type in a number and this number should be formatted correctly to be easier to read.
Not with a comma, with a dot.
I found only currency stuff in the whole research
What you are looking for is probably groupingSeparator of NumberFormatter
let formater = NumberFormatter()
formater.groupingSeparator = "."
formater.numberStyle = .decimal
let formattedNumber = formater.string(from: number)
There's actually much easier solution (there is no need to create NumberFormatter instance) and it takes into account the user's language:
let result = String(format: "%ld %#", locale: Locale.current, viewCount, "views")
Result for value 1000000 with English:
1,000,000
Russian:
1 000 000
p.s. in Android it's exactly the same String.format(Locale.getDefault(), "%,d %s", viewCount, "views")
You can do it with NumberFormatter:
let yourNumber = 123234234234
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = NumberFormatter.Style.decimal
numberFormatter.groupingSeparator = "."
let formattedNumber = numberFormatter.string(from: NSNumber(value:yourNumber))
Details
Xcode 9.2, Swift 4
Xcode 10.2 (10E125), Swift 5
Solution
import Foundation
extension String { var toLocale: Locale { return Locale(identifier: self) } }
extension NumberFormatter {
convenience init(numberStyle: NumberFormatter.Style, groupingSeparator: String?, decimalSeparator: String?) {
self.init()
set(numberStyle: numberStyle, groupingSeparator: groupingSeparator, decimalSeparator: decimalSeparator)
}
convenience init(numberStyle: NumberFormatter.Style, locale: Locale) {
self.init()
set(numberStyle: numberStyle, locale: locale)
}
func set(numberStyle: NumberFormatter.Style, groupingSeparator: String?, decimalSeparator: String?) {
self.locale = nil
self.numberStyle = numberStyle
self.groupingSeparator = groupingSeparator
self.decimalSeparator = decimalSeparator
}
func set(numberStyle: NumberFormatter.Style, locale: Locale?) {
self.numberStyle = numberStyle
self.locale = locale
}
}
extension Numeric {
func format(formatter: NumberFormatter) -> String? {
if let num = self as? NSNumber { return formatter.string(from: num) }
return nil
}
}
Usage
let formatter = NumberFormatter(numberStyle: .decimal, locale: "fr_FR".toLocale)
print(value.format(formatter: formatter))
formatter.set(numberStyle: .decimal, groupingSeparator: " ", decimalSeparator: ".")
print(value.format(formatter: formatter))
Full sample
Do not forget to add the solution code here
func test<T: Numeric>(value: T) {
print("=========================================================")
print("\(T.self), value = \(value)")
let formatter = NumberFormatter(numberStyle: .decimal, locale: "fr_FR".toLocale)
print(value.format(formatter: formatter) ?? "nil")
formatter.set(numberStyle: .currency, locale: "de_DE".toLocale)
print(value.format(formatter: formatter) ?? "nil")
formatter.set(numberStyle: .decimal, groupingSeparator: " ", decimalSeparator: ".")
print(value.format(formatter: formatter) ?? "nil")
}
func print(title: String, value: String?) {
if let value = value { print("\(title) \(value)") }
}
test(value: Int(10000))
test(value: Double(10000.231))
test(value: Float(10000.231))
Result
=========================================================
Int, value = 10000
10 000
10.000,00 €
10 000
=========================================================
Double, value = 10000.231
10 000,231
10.000,23 €
10 000.231
=========================================================
Float, value = 10000.231
10 000,231
10.000,23 €
10 000.231
swift 4
extension Int {
func formatnumber() -> String {
let formater = NumberFormatter()
formater.groupingSeparator = "."
formater.numberStyle = .decimal
return formater.string(from: NSNumber(value: self))!
}
}
For leading zeros (Swift 5.2)
String(format: "%02d", intNumber) // 6 -> "06"
String(format: "%03d", intNumber) // 66 -> "066"
String(format: "%04d", intNumber) // 666 -> "0666"
from: https://stackoverflow.com/a/25566860/1064316
For swift 4, I implemented an extension where I can choose the formatting or use default one if none is selected.
extension Int {
func formatnumber(groupingSeparator: String?) -> String {
let formater = NumberFormatter()
formater.groupingSeparator = (groupingSeparator != nil) ? groupingSeparator! : ","
formater.numberStyle = .decimal
return formater.string(from: NSNumber(value: self))!
}
}
My script as an example:
1) Add extension to project
extension String {
public func subString(startIndex: String.Index, endIndex: String.Index) -> String {
return String(self[startIndex...endIndex])
}
public func subString(_ from: Int, _ to: Int) -> String {
let startIndex = self.index(self.startIndex, offsetBy: from)
let endIndex = self.index(self.startIndex, offsetBy: to)
return String(self[startIndex...endIndex])
}
}
2) Create file Utilites.swift and add my method
public func priceNumFormat(_ number: String)->String{
var formattedNumber = number
var print = number
var prefix = ""
if number.range(of:"-") != nil {
let index = number.index(of:"-")
formattedNumber.remove(at: index ?? formattedNumber.endIndex)
prefix = "-"
}
if formattedNumber.range(of:".") != nil {
let index = formattedNumber.index(of:".")
formattedNumber = formattedNumber.subString(startIndex: formattedNumber.startIndex, endIndex: index ?? formattedNumber.endIndex)
formattedNumber.remove(at: index ?? formattedNumber.endIndex)
}
if formattedNumber.count == 8 //10 000 000
{
let num0 = formattedNumber.subString(0, 1)
let num1 = formattedNumber.subString(2, 4)
let num2 = formattedNumber.subString(5, 7)
print = "\(num0) \(num1) \(num2)"
}
if formattedNumber.count == 7 //1 000 000
{
let num0 = formattedNumber.subString(0, 0)
let num1 = formattedNumber.subString(1, 3)
let num2 = formattedNumber.subString(4, 6)
print = "\(num0) \(num1) \(num2)"
}
if formattedNumber.count == 6 //100 000
{
let num0 = formattedNumber.subString(0, 2)
let num1 = formattedNumber.subString(3, 5)
print = "\(num0) \(num1)"
}
if formattedNumber.count == 5 //10 000
{
let num0 = formattedNumber.subString(0, 1)
let num1 = formattedNumber.subString(2, 4)
print = "\(num0) \(num1)"
}
if formattedNumber.count == 4 //1 000
{
let num0 = formattedNumber.subString(0, 0)
let num1 = formattedNumber.subString(1, 3)
print = "\(num0) \(num1)"
}
if formattedNumber.count == 3 //100
{
print = formattedNumber
}
if prefix.count > 0
{
print = "- \(print)"
}
return print;
}
3) Add code in your UIController
let utils = Utilites()
private func test(){
var price = self.utils.priceNumFormat("-12345678.000")
print("\(price)") //-12 345 678
price = self.utils.priceNumFormat("-1234567.000")
print("\(price)") //-1 234 567
price = self.utils.priceNumFormat("-123456.000")
print("\(price)") //-123 456
price = self.utils.priceNumFormat("-12345.000")
print("\(price)") //-12 345
price = self.utils.priceNumFormat("-1234.000")
print("\(price)") //-1 234
price = self.utils.priceNumFormat("-123.000")
print("\(price)") //-123
}

drawGlyphsForGlyphRange in Swift (Displaying Invisible characters). replaceGlyphAtIndex deprecated

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