Own "Circle The Dot" Game - weird behavior - swift

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

Related

Can't make calculator app in Swift get more than one digit?

So this is the class for my Operation:
class Calculation {
var currentNumber: String = ""
var resultNumber = Int()
var operationInput = String()
func operationIdentifier() {
if operationInput == "=" {
resultNumber = Int(currentNumber)!
print("\(resultNumber)")
} else if operationInput == "+" {
resultNumber += Int(currentNumber)!
print("\(resultNumber)")
} else if operationInput == "-" {
resultNumber -= Int(currentNumber)!
print("\(resultNumber)")
} else if operationInput == "*" {
resultNumber *= Int(currentNumber)!
print("\(resultNumber)")
} else if operationInput == "/" {
resultNumber /= Int(currentNumber)!
print("\(resultNumber)")
} else {
print("Operation does not exist.")
}
print("\(resultNumber)")
}
And this is where I get this button to get the title for numbers:
#IBAction func numberPressed(_ sender: UIButton) {
calculation.currentNumber = (sender.titleLabel?.text!)!
calculation.currentNumber.append(<#T##other: String##String#>)
//calculation.currentNumber.append((sender.titleLabel?.text)!) This is how I use it to append.
calculation.operationIdentifier()
resultTextField.text = "\(calculation.currentNumber)"
}
The append command is for strings as showed in the code section which I use to add to the end of the previous number, and the result is always a double of some Int e.g : If you press 5, it returns 55.
First of all, you are overwriting value here: calculation.currentNumber = (sender.titleLabel?.text!)!. Second, you will need to clear a current number after pressing.
Here is my concept for you(your edited code), you can try it in Playground.
enum Operator: String {
case plus = "+"
case minus = "-"
case multiply = "*"
case divide = "/"
case equal = "="
}
class Calculation {
var currentNumber: String = ""
var resultNumber = Int()
var operationInput = String()
func recalculate() {
if resultNumber == 0 {
resultNumber = Int(currentNumber) ?? 0
return
}
guard let sign = Operator(rawValue: operationInput), let number = Int(currentNumber) else { return }
switch sign {
case .plus: resultNumber += number
case .minus: resultNumber -= number
case .divide: resultNumber /= number
case .multiply: resultNumber *= number
case .equal: resultNumber = number
}
}
func operatorPressed(_ op: Operator) {
recalculate()
operationInput = op.rawValue
currentNumber = ""
print(resultNumber)
}
func numberPressed(_ number: String) {
currentNumber.append(number)
}
}
let c = Calculation()
c.numberPressed("5")
c.numberPressed("5")
c.operatorPressed(.plus)
c.numberPressed("5")
c.operatorPressed(.multiply)
c.numberPressed("5")
c.operatorPressed(.equal)
c.operatorPressed(.plus)
c.numberPressed("5")
c.operatorPressed(.equal)
In numberPressed you are setting currentNumber in from the label text and then appending the same value to it again. It should be something like:
#IBAction func numberPressed(_ sender: UIButton) {
guard let label = sender.titleLabel, let numberString = label.text else{
return
}
calculation.currentNumber.append(numberString)
print("currentNumber = \(calculation.currentNumber)")
}
Also, why do you call calculation.operationIdentifier() in numberPressed? Wouldn't that go in an operatorPressed method?

Swift ViewController Crashes on Load

I'm making a multi conversion tool in iOS to build up my portfolio. However, the distance tab will not load the view. It instantly crashes and gives me two errors.
The second one appears when I try to continue. Below are the errors and my Swift class tied to the controller as well as what the app looks like.
errors
import UIKit
class DistanceViewController: UIViewController, UITextFieldDelegate{
#IBOutlet var userDistance: UITextField!
#IBOutlet var resultLabel: UILabel!
var fromKilometerValue: Measurement<UnitLength>?{
didSet{
milesConversion()
}
}
var fromMileValue: Measurement<UnitLength>?{
didSet{
kilometerConversion()
}
}
override func viewDidLoad() {
super.viewDidLoad()
milesConversion()
kilometerConversion()
}
//Dont forget to drag a gesture recognizer
#IBAction func dismissKeyboard(_sender: UITapGestureRecognizer){
userDistance.resignFirstResponder()
}
let numberFormatter: NumberFormatter = {
let nf = NumberFormatter()
nf.numberStyle = .decimal
nf.minimumFractionDigits = 1
nf.maximumFractionDigits = 1
return nf
}()
func textField(_ userDistance: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool {
let existingTextHasDecimalSeparator = userDistance.text?.range(of: ".")
let replacementTextHasDecimalSeparator = string.range(of: ".")
if existingTextHasDecimalSeparator != nil,
replacementTextHasDecimalSeparator != nil {
return false
} else {
return true
}
}
var toMileValue: Measurement<UnitLength>?{
if let fromKilometerValue = fromKilometerValue{
return fromKilometerValue.converted(to: .miles)
}
else{
return nil
}
}
var toKilometerValue: Measurement<UnitLength>?{
if let fromMileValue = fromMileValue{
return fromMileValue.converted(to: .kilometers)
}
else{
return nil
}
}
func milesConversion(){
if let toMileValue = toMileValue {
resultLabel.text = numberFormatter.string(from: NSNumber(value: Double(userDistance.text!)!))! + " km" + " is " + numberFormatter.string(from: NSNumber(value: toMileValue.value))! + " miles"
}
}
func kilometerConversion(){
if let toKilometerValue = toKilometerValue{
resultLabel.text = numberFormatter.string(from: NSNumber(value: Double(userDistance.text!)!))! + " miles" + " is " + numberFormatter.string(from: NSNumber(value: toKilometerValue.value))! + " km"
}
}
#IBAction func convertKilometers(_ sender: Any) {
if let input = userDistance.text, let value = Double(input) {
fromKilometerValue = Measurement(value: value, unit: .kilometers)
} else {
fromKilometerValue = nil
}
if(toMileValue == nil){
resultLabel.text = "Unable to Convert " + userDistance.text!
}
}
#IBAction func convertMiles(_ sender: Any) {
if let input = userDistance.text, let value = Double(input) {
fromMileValue = Measurement(value: value, unit: .miles)
} else {
fromMileValue = nil
}
if(toKilometerValue == nil){
resultLabel.text = "Unable to Convert " + userDistance.text!
}
}
}
The class and view are mapped properly from what I see. Anybody have any idea?
EDIT: i had old connections that didnt exist in Main.storyboard, i removed them and the view loads just fine!
Check all of your outlet connections. And read stackoverflow.com/questions/32170456/… – rmaddy

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

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.

I'm trying to make ImageProcessor in swift but getting an error

import UIKit
let image = UIImage(named: "sample.png")
// Process the image!
var myRGBA = RGBAImage(image: image!)
let avgRed = 122
let avgGreen = 113
let avgBlue = 51
for y in 0..<myRGBA?.height {
for x in 0..<myRGBA?.width {
let index = y * (myRGBA?.width)! + x
var pixel = myRGBA?.pixels[index]
let redDiff = Int(pixel.red) - avgRed
if (redDiff>0)
{
pixel.red = UInt8( max(0, min(255,avgRed + redDiff * 5)))
myRGBA?.pixels[index] = pixel
}
}
}
let newImage2 = myRGBA?.toUIImage()
The error is saying -> Binary operator '..<' cannot be applied to operands of type 'Int' and 'Int?'
The RGBAImage is running smooth without any errors
I'm writing the code in swift 3
i think you just need to unwrap your optional
import UIKit
let image = UIImage(named: "sample.png")
// Process the image!
var myRGBA = RGBAImage(image: image!)
let avgRed = 122
let avgGreen = 113
let avgBlue = 51
if let unwrapped = myRGBA?{
for y in 0..< unwrapped.height {
for x in 0..< unwrapped.width {
let index = y * (myRGBA?.width)! + x
var pixel = myRGBA?.pixels[index]
let redDiff = Int(pixel.red) - avgRed
if (redDiff>0)
{
pixel.red = UInt8( max(0, min(255,avgRed + redDiff * 5)))
myRGBA?.pixels[index] = pixel
}
}
}
}
let newImage2 = myRGBA?.toUIImage()
I changed few things now the code below works for me
import UIKit
let image = UIImage(named: "sample.png")
// Process the image!
var myRGBA = RGBAImage(image: image!)
let avgRed = 122
let avgGreen = 113
let avgBlue = 51
if let unwrapped = myRGBA {
for y in 0..<unwrapped.height {
for x in 0..<unwrapped.width {
let index = y * (myRGBA?.width)! + x
var pixel = myRGBA?.pixels[index]
let redDiff = Int((pixel?.red)!) - avgRed
if (redDiff>0)
{
pixel?.red = UInt8( max(0, min(255,avgRed + redDiff * 5)))
myRGBA?.pixels[index] = pixel!
}
}
}
}
let newImage2 = myRGBA?.toUIImage()
Reason for the error:
Optional comparison is not supported by default in Swift 3.0.
So you need to overload the comparison operators on your own or swift migrator will do it for you if you are migrating your code to Swift 3.0.
Overloaded methods look like this:
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
fileprivate func > <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l > r
default:
return rhs < lhs
}
}

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