Swift dictionary change the variable of the value - ios

I'm sorry if it's already discussed, but i don't find the solution
I have a struct:
struct Point2D
{
var x: Double = 0.0
var y: Double = 0.0
}
And I have a class also:
class Letter
{
var points3D: Dictionary<String,Point3D> = [:]
var points2D: Dictionary<String,Point2D> = [:]
var edges: Dictionary<String,Edge> = [:]
}
In some view:
class GraphicView: UIView {
var letter:Letter!
override func drawRect(rect: CGRect) {
for (name,point3d) in letter.points3D {
//Says String isn't convertable to DictionaryIndex<String,Point2D>
letter.points2D[name]!.x = point3d.x * (perspective / (perspective - point3d.z)) + imageWidth/2
letter.points2D[name]!.y = point3d.y * (perspective / (perspective - point3d.z)) + imageHeight/2
}
}
I'm trying to find some value for key (I know it exists) and change variables of this value to another. What's wrong?

Maybe perspective, imageWidth or imageHeight is not Double?
try:
class GraphicView: UIView {
var letter:Letter!
override func drawRect(rect: CGRect) {
for (name,point3d) in letter.points3D {
letter.points2D[name]!.x = point3d.x * (Double(perspective) / (Double(perspective) - point3d.z)) + Double(imageWidth)/2
letter.points2D[name]!.y = point3d.y * (Double(perspective) / (Double(perspective) - point3d.z)) + Double(imageHeight)/2
}
}

Related

How to add custom value in charts for bottom label in swift iOS

I have to show [0, 25, 50, 75, 100] with dollar sign in Charts using swift iOS
var xValue = [25.0, 50.0]
var dollarValue = ["$25", "$50", "$75", "$100"]
Added in view didLoad
rightAxis.valueFormatter = IndexAxisValueFormatter(values: dollarValue)
func updateCharts() {
var chartDataEntry = [BarChartDataEntry]()
let spaceForBar = 10.0;
for i in 0..<xValue.count {
let value = BarChartDataEntry(x: Double(i) * spaceForBar, y: xValue[i])
chartDataEntry.append(value)
}
let chartDataSet = BarChartDataSet(entries: chartDataEntry, label: "10x In-Store ")
chartDataSet.colors = ChartColorTemplates.material()
let chartMain = BarChartData(dataSet: chartDataSet)
chartMain.barWidth = 5.0
horizontalBarChartContainnerView.animate(yAxisDuration: 0.5)
horizontalBarChartContainnerView.data = chartMain
}
I wanted to show 0 $25 $50 $75 $100 instead of 0 10 20 30 40 50
You need to create a custom formatter
public class CustomAxisValueFormatter: NSObject, AxisValueFormatter {
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
return "$\(Int(value))"
} }
and set the axis value formatter to your custom formatter
xAxis.valueFormatter = CustomAxisValueFormatter()
This code is working fine
public class CustomAxisValueFormatter: AxisValueFormatter {
var labels: [String] = []
public func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let count = self.labels.count
guard let axis = axis, count > 0 else {
return ""
}
let factor = axis.axisMaximum / Double(count)
let index = Int((value / factor).rounded())
if index >= 0 && index < count {
return self.labels[index]
}
return ""
}
}
Add inside of viewDidLoad()
rightAxis.axisMaximum = 4
let customAxisValueFormatter = CustomAxisValueFormatter()
customAxisValueFormatter.labels = ["$25", "$80", "$75", "$100"]

Charts: How to set colors of left and right axis labels in LineChartView?

My graph is using custom IAxisValueFormatters to set the left and right axis labels (below). I've tried using the labelTextColor properties on left and right axis (below) but they're not changing the label's font color.
class myViewController {
func initalizeGraph() {
//omitted for brevity
let leftYAxisValueFormatter = SpeedAndHRLeftYAxisValueFormatter(measurementSystem: measurementSystem)
lineChartView.leftAxis.labelTextColor = iPhoneThemeColor1
lineChartView.leftAxis.valueFormatter = leftYAxisValueFormatter
let rightYAxisValueFormatter = SpeedAndHRRightYAxisValueFormatter()
lineChartView.rightAxis.labelTextColor = iPhoneThemeColor2
lineChartView.rightAxis.valueFormatter = rightYAxisValueFormatter
}
}
class SpeedAndHRLeftYAxisValueFormatter: IAxisValueFormatter {
//left side is speed
//multiplu value x 40 (.5 * x = 20?)
var measurementSystem: MeasurementSystem
init(measurementSystem: MeasurementSystem) {
self.measurementSystem = measurementSystem
}
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let intValue = Int(value * 40)
var suffixString = ""
switch measurementSystem {
case .US:
suffixString = "mph"
case .Metric:
suffixString = "km/h"
}
return "\(intValue)" + " " + suffixString
}
}
class SpeedAndHRRightYAxisValueFormatter: IAxisValueFormatter {
//right side is HR
func stringForValue(_ value: Double, axis: AxisBase?) -> String {
let intValue = Int(value * 210)
return "\(intValue)" + " " + "bpm"
}
}
You have to add following code in viewDidLoad() method. This is working perfactly for me in one of my project.
override func viewDidLoad() {
let leftYAxisValueFormatter = SpeedAndHRLeftYAxisValueFormatter(measurementSystem: measurementSystem)
lineChartView.leftAxis.valueFormatter = leftYAxisValueFormatter
lineChartView.leftAxis.labelTextColor = iPhoneThemeColor1
let rightYAxisValueFormatter = SpeedAndHRRightYAxisValueFormatter()
lineChartView.rightAxis.valueFormatter = rightYAxisValueFormatter
lineChartView.rightAxis.labelTextColor = iPhoneThemeColor2
}
Also, update you code for class create as follow:
#objc(LineChartFormatter)
class SpeedAndHRRightYAxisValueFormatter:NSObject, IAxisValueFormatter {
....
//Your Code
....
}
#objc(LineChartFormatter)
class SpeedAndHRLeftYAxisValueFormatter:NSObject, IAxisValueFormatter {
....
//Your Code
....
}
I hope this will help you.

Getting EXC_BAD_ACCESS while the object exists

I'm developing a music sequencer with a standard piano roll type UI.
It was working well until I made some changes in the model side but it suddenly started to report EXC_BAD_ACCESS at (seemingly) unrelated part.
What's strange is all the necessary variables have their values properly and actually I can print values with po.
In my understanding, EXC_BAD_ACCESS happens when an object doesn't exist, so this seems quite strange.
My question is:
Is it common to EXC_BAD_ACCESS even the values are there?
If that's the case what is the possible situation to cause that?
Any suggestion is helpful. Thanks
[Below are the codes]
In my subclass of UICollectionViewLayout:
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// note cells
let cv = self.collectionView as! YMPianoRollCollectionView;
let pianoRoll = cv.pianoRollViewController;
// Call the below func to get the indexes of the Note Objects included in the specified rect
let indexArray: Array<Int> = pianoRoll!.getNoteIndexes(inRect:rect, useOnlyStartTime: false);
var retArray : [UICollectionViewLayoutAttributes] = []
for i in indexArray {
if let _ = pianoRoll?.pattern.eventSequence[i] as? YMPatternEventNoteOn {
retArray.append( self.layoutAttributesForPatternEventInfo(i) )
}
}
return retArray
}
In my "piano roll" class which contains UICollectionView
func getNoteIndexes(inRect rect:CGRect, useOnlyStartTime: Bool) -> Array<Int> {
//
// Transform given values into musical values
//
let musicRange :YMMusicalValueRange = screenInfo.getMusicalRange(rect);
let startTime = musicRange.origin.time;
let endTime = musicRange.origin.time + musicRange.size.timeLength;
let lowNoteU = musicRange.origin.noteNumber;
let highNoteU = musicRange.origin.noteNumber + musicRange.size.numberOfNotes;
var retArray : [Int] = []
for i in 0..<pattern.eventSequence.count {
if let e = pattern.eventSequence[i] as? YMPatternEventNoteOn {
//
// Prepare ranges
//
let noteNo = e.noteNo; //<- App Crashes Here with BAD_ACCESS
let noteStTime = e.time;
let noteEnTime = e.time + e.duration;
let targetNoteRange = Range<Int>(uncheckedBounds: (lowNoteU, highNoteU));
let targetTimeRange = Range<Int64>(uncheckedBounds: (startTime, endTime))
let noteTimeRange = Range<Int64>(uncheckedBounds: (noteStTime, noteEnTime))
//
// Check the match
//
let noteMatches = targetNoteRange.contains(noteNo);
let timeMatches = useOnlyStartTime ? targetTimeRange.contains(noteStTime)
: targetTimeRange.overlaps(noteTimeRange)
if noteMatches && timeMatches {
retArray.append( i );
NSLog("XXX Found: note \(noteNo) at \(e.time)");
}
}
}
return retArray;
}
Error:- The object states when it crashed
EDIT
Here's the YMPatternEventNoteOn declaration
class YMPatternEvent : Codable, CustomDebugStringConvertible {
var time : YMSequenceTime = 0
// Some utility funcs follow
// ...
}
class YMPatternEventNoteOn : YMPatternEvent {
var noteNo : Int = 64
var velocity : Int = 127
var duration : YMSequenceTime = 480
var tempBendId : Int = 0;
var tempVibratoId : Int = 0;
var tempArpeggioId : Int = 0;
convenience init(time :YMSequenceTime, noteNo : Int, velocity: Int, duration: YMSequenceTime) {
self.init();
self.time = time;
self.noteNo = noteNo;
self.velocity = velocity;
self.duration = duration;
}
// Other methods follow
// ...
}
EDIT2
Note event is created by the user's action
//
// In YMPattern object
//
func insertNote(time:YMSequenceTime, noteNo:Int, velocity:Int, duration:YMSequenceTime) -> Int
{
let onEvent = YMPatternEventNoteOn(time: time, noteNo: noteNo, velocity: velocity, duration: duration);
let retIndex = insertEvent(onEvent);
return retIndex;
}
func insertEvent(_ event: YMPatternEvent) -> Int {
let atTime = event.time;
var retIndex : Int = 0;
if(eventSequence.count<1){
// If it's the first event just add it
eventSequence.append(event);
retIndex = 0;
} else {
// If any event already exists, insert with time order in consideration
var i : Int = 0;
while( atTime > eventSequence[i].time ){
i += 1;
if( i >= eventSequence.count ){
break;
}
}
retIndex = i;
eventSequence.insert(event, at: i)
}
}
//
// In pianoroll view controller
//
func actionButtonReleased(afterDragging: Bool) {
let values:YMMusicalValuePoint = screenInfo.getMusicalPosition(cursorPosition);
// insert new event with default velocity and duration
let _ = pattern.insertNote(time: values.time, noteNo: values.noteNumber, velocity: 127, duration: screenInfo.timeTicsPerDivision());
collectionView.reloadData();
}
I've solved it in a way.
By setting the optimization level to "Whole Module Optimization" it stopped reporting the error.
I don't know what is happening internally but if someone is having the same issue, this might work as a quick fix.

Unable to assign class objects to dictionary member of another class object in Swift 3

I have a class ChessSquare with its own attributes and methods. I have another class ChessBoard, which has an attribute of dictionary type.
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
.....
One of the methods of the ChessBoard class builds the chessSquare object and assigns it to chessBoard object as below.
self.squares[squareName] = chessSquare
Before Swift3 this code was working fine. After upgrading to Swift3 the assignment stopped working.
Using breakpoints I see that chessSquare objects are built as expected. squareName variable has the expected value. The "self", which is the chessBoard object has proper initial values. But the dictionary assignment gets skipped without any error in the above mentioned code.
Is this anything related to Swift3. I browsed and could not get specific solution to my case.
Adding more code for clarity.
class ChessSquare {
let squareColor: UIColor
let squareShade: Int
let squareName: Int
let squareSize: CGSize
let minXY: CGPoint
let maxXY: CGPoint
let squareOrigin: CGPoint
var hasPiece = false
weak var chessPiece: ChessPiece?
let squareSprite: SKSpriteNode
let squareType: SquareType
//more data members
init(squareColor: UIColor, squareShade: Int, squareName: Int, squareSize: CGSize, minXY: CGPoint, maxXY: CGPoint, squareOrigin: CGPoint, squareSprite: SKSpriteNode, squareType: SquareType) {
self.squareColor = squareColor
self.squareShade = squareShade
self.squareName = squareName
self.squareSize = squareSize
self.minXY = minXY
self.maxXY = maxXY
self.squareOrigin = squareOrigin
self.squareSprite = squareSprite
self.squareType = squareType
}
//more methods
}
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
var byteBoard: [UInt8] = [UInt8](repeating: UInt8(0), count: 66)
var byteBoardHashVal: Int = 0
// [ byteBoardHashVal : ((byteBoard, evalnVal), repCnt) ]
var byteBoards: [Int : (([UInt8],Double?), Int)] = [:]
var TT: [Int : Double] = [:]
var TTReuseCount = 0
var whitePawnlessFileByte: UInt8 = 255
var blackPawnlessFileByte: UInt8 = 255
var maxXY: CGPoint = CGPoint()
var minXY: CGPoint = CGPoint()
var oldTouchedSquare: ChessSquare?
init() {
squares = [:]
byteBoard = [UInt8](repeating: UInt8(0), count: 66)
byteBoardHashVal = 0
byteBoards = [:]
TT = [:]
TTReuseCount = 0
whitePawnlessFileByte = 255
blackPawnlessFileByte = 255
}
func drawFlippedChessBoard(_ view: SKView, scene: GameScene) {
....
....
for row in 0..<8 {
for col in 0..<8 {
let squareName = row * 8 + col
....
let chessSquare = ChessSquare(squareColor: currentColor, squareShade: squareShade, squareName: squareName, squareSize: squareSize, minXY: minXY, maxXY: maxXY, squareOrigin: squareOrigin, squareSprite: squareSprite, squareType: squareType)
....
self.squares[squareName] = chessSquare
....
}
....
}
....
}
//more methods
}
I have a difficulty in understanding if squareName is a string or a integer. It sounds like a string, I would suggest to name it chessSquareIndex or squareIndex if in case it is Int and you want to index chess square.
Based on your comment above I did a small test to verify that it works correctly if squareName is Int and here is the result,
class ChessSquare { }
class ChessBoard {
var squares: [ Int : ChessSquare ] = [:]
func getChessSquare(at squareIndex: Int) {
let chessSquare = ChessSquare()
return squares[squareIndex] = chessSquare
}
}
And this seems to work fine with Swift 3. Also notice that it is not required to call self, unless you are inside escaping closures.

Why is my GPA Double value not working correctly in Swift?

why isn't this code working when I addA for the first time it works but then it goes to 2.66 and keeps going down from there when it should stay at 4.0.
import UIKit
//Quantity of A A- B+ B B- C+ C C- D+ D D- F
var gradesQuantity = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
var gradesAdded = 0.0
var gpa = 0.0
func addGrades(grades: [Double]) -> Double {
for grade in grades {
gradesAdded += grade
if gradesQuantity[0] <= 0 {
gradesQuantity[0] = 0
}
}
return gradesAdded
}
func calcGPA(grades: [Double]) -> Double {
gpa = (grades[0] * 4.0 + grades[1] * 3.7 + grades[2] * 3.3 + grades[3] * 3.0 + grades[4] * 2.7 + grades[5] * 2.3 + grades[6] * 2.0 + grades[7] * 1.7 + grades[8] * 1.3 + grades[9] * 1.0 + grades[10] * 0.7) / gradesAdded
return gpa
}
class ViewController: UIViewController {
#IBOutlet weak var GPALabel: UILabel!
#IBOutlet weak var GPANumber: UILabel!
#IBOutlet weak var AQuantity: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func AddA() {
gradesQuantity[0] += 1.0
addGrades(gradesQuantity)
let x = calcGPA(gradesQuantity)
GPANumber.text = String(x)
AQuantity.text = String(Int(gradesQuantity[0]))
}
#IBAction func SubtractA() {
gradesQuantity[0] -= 1.0
addGrades(gradesQuantity)
let x = calcGPA(gradesQuantity)
GPANumber.text = String(x)
AQuantity.text = String(Int(gradesQuantity[0]))
}
}
can somebody please help i put this in playground and add code under it that is the same in the buttons and it works but in the project it doesnt work. This leads me to believe the problem is in the addA and subtractA buttons.
You are writing functions with side effects (modifying external variables). Try to avoid that. Also, you may want to have a bit more structure in your program. Associate positional value of gradesQuantity to a letter grade is ok, but you can do better with a Dictionary:
enum GradeLetter: Double {
case A = 4.0
case AMinus = 3.7
case BPlus = 3.3
case B = 3
case BMinus = 2.7
case CPlus = 2.3
case C = 2
case CMinus = 1.7
case DPlus = 1.3
case D = 1
case DMinus = 0.7
case F = 0
}
func calculateGPA(grades: [GradeLetter: Int]) -> Double {
let courseCount = grades.reduce(0.0) { aggregate, grade in
return aggregate + Double(grade.1)
}
let totalPoint = grades.reduce(0.0) { aggregate, grade in
return aggregate + grade.0.rawValue * Double(grade.1)
}
return totalPoint / courseCount
}
Let's say you have 1 A, 2 A- and 3 B:
var grades = [GradeLetter: Int]()
grades[.A] = 1
grades[.AMinus] = 2
grades[.B] = 3
print(calculateGPA(grades))
I think the root cause of your problem is that you are doing too much work. If you delete the addGrades function (which isn't really adding any functionality as far as I can tell) and replace your AddA and SubtractA functions with the following, then it will work as intended:
#IBAction func AddA() {
gradesQuantity[0] += 1.0
gradesAdded += 1.0
GPANumber.text = String(calcGPA(gradesQuantity))
AQuantity.text = String(Int(gradesQuantity[0]))
}
#IBAction func SubtractA() {
gradesQuantity[0] -= 1.0
gradesAdded -= 1.0
GPANumber.text = String(calcGPA(gradesQuantity))
AQuantity.text = String(Int(gradesQuantity[0]))
}
For further development, I would recommend creating a subclass array that has functions to add and subtract grades and return the GPA. This should make things much more readable and reusable.

Resources