I am trying to store in integer value in a SKLabelNode but I keep geting an error that I can only store strings. I need it as an integer later. Here is my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
var comScore = SKLabelNode()
comScore.fontName = "Pong Score"
comScore.text = 0
comScore.fontSize = 100
comScore.color = SKColor.white
comScore.position = CGPoint(x: 200, y: 220)
comScore.zPosition = 3
addChild(comScore)
}
}
Help would be greatly appreciated as I am new to SpriteKit.
An SKLabelNode's text property is a string, not an Int. You can make it a string by adding quotes:
comScore.text = "0"
or you can convert a Int variable to a string:
comScore.text = String(myIntValue)
I fixed this issue by storing it as a String initaly and then using the following code converted it back and forth:
var comScoreInt: Int = Int(comScore.text!)!
comScoreInt += 1
comScore.text = String(comScoreInt)
In addition to the other answers regarding Ints and Strings, if you stick a property watcher on your score, you can get the label updated automatically when you update the score:
var comScoreInt: Int {
didSet{
comScore.text = String(comScoreInt)
}
}
Edit:
If, as per your own answer, you want to initialise the score from the value in the score label, then make the integer score a computed property:
var comScoreInt -> Int {
get {
return Int(comScore.text!)
}
set(newScore) {
comScore.text = String(newScore)
}
}
Now you can retrieve comScoreInt and it will return the value in the score label and set comScoreInt and it will update the label.
Swift 3
Extensions are your friend:
extension String {
func integerValue() > Int? {
return Int(trimmingCharacters(.whitespacesAndNewlines))
}
static func + (left: String, right: Int) -> String {
guard let value = left.integerValue() else {return left}
return "\(value + right)"
}
static func += (inout left: String, right: Int) {
left = left + right
}
}
Now you can quickly add to a String doing label.text += 1
You end up losing some protections doing it this way though, since you would normally expect String + Int to throw you an error, so you may want to think about the security of this process, and perhaps either subclass String to NumberString so that only a certain type of string can be added with an Int,
or add some error handling into the extensions provided.
You can even take this one step further, and parse out the Int from a String (e.g. "Points: 1") and change the value.
This answer should not be seen as the definitive answer to use for your problem, I am basically offering a basic building block alternative that you may want to consider in your development.
Related
Here is what my class looks like:
class Card : Object {
#objc dynamic var tags: String = ""
#objc dynamic var set_id: String = ""
}
I want to return number of tags from all Cards with forwarded set_id.
Here is the method:
func totalTags() -> String {
var tagCounter: Int = 0
let realm = try? Realm()
let totalCards = realm!.objects(Card.self).filter("set_id = '\(setId)'") //all Cards with selected set_id, set_id is global var.
for card in 0...totalCards.count {
//every 'card' has tags, but there there can me more tags,
//like : tags="one,twitter,world,Europe"...
//And I want to count all of them for every 'card'
let result = realm.objects(Card.self).filter() //How to filter?
tagCounter += //what? result.count or something?
}
return String(tagCounter)
}
I understand that tags: String contains comma separated elements and you want to find the number of elements.
You can do that by iterating over totalCards. For each card, split the tags into an array and count the number of elements.
for card in totalCards {
tagCounter += card.tags.components(separatedBy: ",").count
}
components(separatedBy:) documentation
I know this was already answered but I just want to share how to do it my own way.
let tagsCount = totalCards.map { $0.tags.components(separatedBy: ",") }.flatMap { $0 }.filter { !$0.isEmpty }.reduce(into: 0, { result, _ in
result += 1
})
Thanks. Happy coding :)
It has been a long time since I have coded in iOS and I am upgrading the old app to swift 3. I am really struggling with one issue of using optional variables.
I have a textfield which is optional. I want it unwrapped into a non-optional Int so that I can use it in the other functions for calculation.
#IBOutlet weak var txtRuns: UITextField!
func sendScore()
{
let runs = txtRuns.text!
let overs = txtOvers.text!
let balls = txtBalls.text!
let wkts = txtWkts.text!
let target = txtTarget.text!
let totalOvers = txtTotalOvers.text!
let strData = "S|R\(runs)" + getOptionalScoreData(
runs: Int(runs),
wkts: Int(wkts),
overs: Int(overs),
balls: Int(balls),
target: Int(target),
totalOvers: Int(totalOvers)
)
}
func getOptionalScoreData(runs: Int, wkts: Int, overs: Int, balls: Int, target: Int, totalOvers: Int) -> String
{
if ( runs == 0 ) {
return getCurrentRunRate(runs: runs)
}
return "";
}
As you can see, I have so many functions to call and I want this textfield to turn into non-optional INT.
Now I have tried several options that I read over here but the error messages only change. The problem didn't solve.
Current ERROR is
The value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
Please help.
Thanks
.................
Please note that I don't think nested check is a nice idea here because all these variables are independent of each other. One can be nil but other can be still passed and used.
If I nest checks like this, it means that no other value will be passed if runs are nil.
if let runs = txtRuns.text, let runsInInt = Int(runs) {
if let overs = txtOvers.text, let oversInInt = Int(overs) {
if let wkts = txtWkts.text, let wktsInInt = Int(wkts) {
strData = "S|R\(runs)\(overs)\(wkts)" + getOptionalScoreData( runs: runsInInt, overs: oversInInt, wkts: wktsInInt)
}
If you are frequently getting value from UITextField as Int, you can add an extension as follows:
extension UITextField {
var intValue: Int {
get {
if let text = self.text {
return Int(text) ?? 0
}
return 0
}
set {
self.text = String(newValue)
}
}
}
You can add the above as private extension in your viewcontroller too. Now you can rewrite your code as:
func sendScore() {
let strData = "S|R\(txtRuns.intValue)\(overs.intValue)\(wkts.intValue)" + getOptionalScoreData(
runs: txtRuns.intValue,
wkts: wkts.intValue,
overs: overs.intValue,
balls: balls.intValue,
target: target.intValue,
totalOvers: totalOvers.intValue)
)
}
Int(runs) call constructor of Int following:
public init?(_ text: String, radix: Int = default)
Because String to Int might failed due to the String might not a valid integer.
How would you deal with it?
You can reference Sallu's comment.
user ! to guarantee the String in UITextField is absolute a valid integer, or app crash.
runs: Int(runs)!
user ?? to give a default value if the String in UITextField is not a valid integer.
runs: Int(runs) ?? 0
In the case the default value is 0
A method should calculate the price of all the items in a class’s property. Unfortunately, my knowledge when it comes to programming is ultra limited. Any help would be very much appreciated.
Thanks in advance.
In Swift3
using reduce
func priceBeforeDiscounts() -> Int {
return items.reduce(0) { (result, item) in
return result + item.priceInPence
}
}
using for in loop
func priceBeforeDiscounts() -> Int {
var sum: Int = 0
for item in items {
sum += item.priceInPence
}
return sum
}
Here you have some useful code (prepared in playground) for you:
// This is your item class
class Item {
var priceInPence: Int = 0
init(price: Int) {
priceInPence = price
}
}
// This is your array of items
var items = [Item]()
// Here I create items with given price
for _ in 0...3 {
items.append(Item(price: 2))
}
// This is the most interesting part for you, here you sum up prices from items in array
let sum = items.reduce(0, { $0 + $1.priceInPence })
print(sum)
I created this code in playground, so you can copy/past this code to your playground and play with it. Having my example you should be able to update your source code.
i have an array, var hoursPlayed = String
they are in a tableView and are all numbers, how would i add the numbers in that array together to get the average of hours played????? in Swift 2
You could use reduce:
let sum= hoursPlayed.reduce(0.0,combine:{$0+Float($1)!})
Basically you are iterating through the array and accumulating all the values. Since it is an array of strings,for simplicity I've force unwrapped to a Float, but you must check for the optional. The reduce function takes a closure as argument with 2 parameters. The dollar sign means take the first and the second and sum them.
Now you can easily divide to the number of elements in the array to have an avergae.
If you are in objC world it would be nice use key value coding and the #avg operator.
[UPDATE]
As Darko posted out the first version won't compile. The error was converting the first argument to a Float, since reduce takes an initial value and I put it as Float there is no need for further conversion.
let array = ["10.0", "30.0"]
if array.count > 0 {
let average = array.reduce(0.0, combine: {$0 + (Double($1) ?? 0.0)}) / Double(array.count)
print(average) // 20.0
}
$0 does not need to be converted because it is guaranteed that it's always Double. $0 is inferred from the initial value, which is declared as 0.0: Double.
array.count has to be checked to guard against a division thru 0.
I'd use a combination of flatMap to convert the strings to Doubles and reduce to add them up:
let doubles = array.flatMap { Double($0) }
let average = doubles.reduce(0.0, combine:+) / Double(doubles.count)
Using flatMap protects you from entries in array that can't be converted to Double If you know they all convert you can simplify it to:
let average = array.map({ Double($0)! }) / Double(array.count)
One final option is to extend Array with an average function if that seems like something you'll be more generally using, and use it in combination with flatMap and/or map:
protocol ArithmeticType {
static func zero() -> Self
func +(lhs:Self, rhs:Self) -> Self
func -(lhs:Self, rhs:Self) -> Self
func /(lhs:Self, rhs:Self) -> Self
func *(lhs:Self, rhs:Self) -> Self
init(_ number:Int)
}
extension Double : ArithmeticType {
static func zero() -> Double {
return 0.0
}
}
extension Array where Element : ArithmeticType {
func average() -> Element {
return reduce(Element.zero(), combine:+) / Element(count)
}
}
let avg = array.flatMap { Double($0) }.average()
Modified Darko's approach, which take in account if String is convertible to Double, or not. For an empty array it returns 0.0
let array = ["10.0", "31.2", "unknown", ""]
func avg(arr: [String])->Double {
let arr = array.flatMap(Double.init)
var avg = 0.0
if arr.count > 0 {
avg = arr.reduce(0.0, combine: + ) / Double(arr.count)
}
return avg
}
let a = avg(array)
print(a) // 20.6
I'm playing around with custom cells.
With the great help from the stackoverflow community i've been able to put some code together. I'm able to fetch array values from a text string into a custom cell uilabel and uibutton, but the issue is - the fetched result is always the last object in the array.
Here is the code
func setUpQuestion()
{
// setting variables
var Question: String?
var option1: String?
// using a text string with custom separators
let text = ">>Here is the grocery question\n>>and another one\n--Apples\n-
-Oranges\n[pickApples]pickOranges\n[pickApples2]"
// splitting this string into four different arrays depending on the separator
let lines = split(text) { $0 == "\n" }
for line in lines {
if line.hasPrefix(">>") {
Question = line.substringFromIndex(advance(line.startIndex, 2))
} else if line.hasPrefix("[") {
if let index = line.rangeOfString("]")?.startIndex {
option1 = line.substringWithRange(Range<String.Index>(
start: advance(line.startIndex, 1), end: index))
}
}
}
// creating variables for appending the values - here I'm using a custom class called QuestionMark created in a separate .swift file
var question1 = QuestionMark(Question: Question!, option:option1!)
var question2 = QuestionMark(Question: Question!, option:option1!)
// appending the values into uilabel and uibutton in the custom cell
arrayOfQuestions.append(question1)
arrayOfQuestions.append(question2)
}
// regular tableView protocol functions
func tableView(tableView: UITableView, numberOfRowsInSection
section: Int) ->Int
{
return arrayOfQuestions.count
}
func updateCount(){
if let list = mainTableView.indexPathsForSelectedRows() as? [NSIndexPath] {
rowsCount.text = String(list.count)
}
}
func tableView(tableView: UITableView, cellForRowAtIndexPath
indexPath: NSIndexPath) -> UITableViewCell
{
let cell: CustomCellForTableViewTableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! CustomCellForTableViewTableViewCell
// the SetCell function i'm using here was created in a separate .swift file
let quest = arrayOfQuestions[indexPath.row]
cell.setCell(quest.Questme!, optionone: quest.optionize!)
cell.optionOne.backgroundColor = UIColor.redColor()
return cell
}
Here are the additional codes i'm using for the class and setCell function
class QuestionMark
{
var Questme: String?
var optionize: String?
init(Question: String, option: String)
{
self.Questme = Question
self.optionize = option
}
// separate swift file
func setCell(Question: String, optionone: String)
{
self.mainText.text = Question
self.optionOne.setTitle(optionone, forState:UIControlState.Normal)
}
As a result in both cells i'm getting the last object from the text string and it looks like this
And another one - PickApples2
And another one - PickApples2
How do i start appending cells from the first array value and then move forward to second,third,fourth ?
Any ideas are greatly appreciated.
Thank you.
First of all, the syntax of the text to parse is pretty complicated ;-) …
Second of all, the problem to get always the last object is that you create the array of questions after the repeat loop. At that moment the variables question and option contain always the last found string.
Here a solution:
After getting a question a new object QuestionMark is created and appended to the array (without the optionize property)
After getting an option the appropriate QuestionMark object is fetched from the array by an index counter, the property optionize is set and the counter is increased.
Two notes:
Variable names should always start with a lowercase letter. Even the syntax highlighter of StackOverflow follows that naming convention.
In my solution all variables are non-optionals.
class QuestionMark
{
var questme: String
var optionize: String
init(question: String, option: String = "")
{
self.questme = question
self.optionize = option
}
...
var arrayOfQuestions = [QuestionMark]()
func setupQuestion() {
let text = ">>Here is the grocery question\n>>and another one\n--Apples\n--Oranges\n[pickApples]pickOranges\n[pickApples2]"
// splitting this string into four different arrays depending on the separator
var counter = 0
var question = ""
var option = ""
let lines = split(text) { $0 == "\n" }
for line in lines {
if line.hasPrefix(">>") {
question = line.substringFromIndex(advance(line.startIndex, 2))
let questionMark = QuestionMark(question: question)
arrayOfQuestions.append(questionMark)
} else if line.hasPrefix("[") {
if let index = line.rangeOfString("]")?.startIndex {
option = line.substringWithRange(Range<String.Index>(
start: advance(line.startIndex, 1), end: index))
let questionMark = arrayOfQuestions[counter]
questionMark.optionize = option
counter++
}
}
}
}