I'm working with counting table and have some problem. How to fix 'Fatal error: String index is out of bounds' - ios

I'm working with counting label String. When I run my project all my text shows and at the end it crashes with:
'Fatal error: String index is out of bounds'
on the line:
startText += String(endText[index])
in the handleUpdate method.
I don't understand why.
class AboutViewController: BaseListController {
let countingLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.textAlignment = .center
label.font = UIFont.boldSystemFont(ofSize: 18)
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(countingLabel)
countingLabel.frame = view.frame
let displayLink = CADisplayLink(target: self, selector: #selector(handleUpdate))
displayLink.add(to: .main, forMode: .default)
}
var startText = " "
let endText = "Hey! I need your help..."
var startValue = 0
#objc func handleUpdate(){
let endTextValue = endText.count - 1
let index = endText.index(endText.startIndex, offsetBy: startValue)
self.countingLabel.text = "\(startText)"
// Error on the following line:
startText += String(endText[index])
startValue += 1
if startValue > endTextValue{
startText = endText
}
print(endTextValue)
}
let animationStartDate = Date()
}

You are calling handleUpdate on a CADisplayLink, it will continue to be called after startValue has been incremented beyond the length of the string. You do have a check for startValue > endTextValue but this is after you have already tried to index using startValue, so you get a crash.
You need to remove the CADisplayLink handler once its job is done, which means you need to keep a reference to it.
var startText = ""
let endText = "Hey! I need your help..."
var startValue = 0
var displayLink: CADisplayLink?
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(countingLabel)
countingLabel.frame = view.frame
self.displayLink = CADisplayLink(target: self, selector: #selector(handleUpdate))
self.displayLink!.add(to: .main, forMode: .default)
}
#objc func handleUpdate(){
guard startValue < endText.count else {
self.displayLink?.invalidate()
self.displayLink = nil
return
}
let index = endText.index(endText.startIndex, offsetBy: startValue)
startText += String(endText[index])
self.countingLabel.text = "\(startText)"
startValue += 1
let animationStartDate = Date()
}

Related

Hangman Game letters reset each time the button is pressed [duplicate]

This question already exists:
I am making a HangMan game and I'm having problems with letters being doubled each time it loops [closed]
Closed last month.
So I've been creating a hangman game and been having problems with each time a user guesses a correct letter again, the previous correct letter/letters gets removed.??
I tried a lot of if elses to hold each index of the letter and append if its correct but that did not work. So in general let's say a user got their first letter correct which was A. This is what happens..
XXXAXXAX
But the Second letter they get correct, lets say B this happens...
XBXXXXXX
they are supposed to bring A along to the next iteration and so onn.
can anyone see what's the problem? Here's my code below..
class ViewController: UIViewController {
var allWords = [String]()
var usedLetters = [String]()
var startWords = [String]()
var promptWord = String()
var randomWord = ""
override func viewDidLoad() {
super.viewDidLoad()
EnterGuess()
fileWork()
print(randomWord)
title = "GUESS A Letter: ?????????)"
}
func fileWork() {
if let startWordsURL = Bundle.main.url(forResource: "start", withExtension: "txt") {
if let startWords = try? String(contentsOf: startWordsURL) {
allWords = startWords.components(separatedBy: "\n")
let wordssss = startWords.randomElement()
randomWord = allWords.randomElement()!
}
}
}
func EnterGuess() {
let ac = UIAlertController(title: "Guess a letter", message: nil, preferredStyle: .alert)
ac.addTextField()
var submitGuess = UIAlertAction(title: "Submit", style: .default) {_ in
guard var answer = ac.textFields?[0].text else {return }
answer
self.submit(answer)
self.EnterGuess()
}
ac.addAction(submitGuess)
present(ac, animated: true)
}
func submit(_ answer: String) {
let guessedLetter = answer
let wordArray = randomWord.map(String.init)
var hidden = Array(repeating: "x", count: randomWord.count)
for index in hidden.indices {
if wordArray[index] == answer {
hidden[index] = wordArray[index]
}
}
print(hidden.joined())
// print(wordArray)
title = String(describing: hidden.joined())
}
}
The problem is that you are "re-setting" the hidden string with each letter guess.
Instead, you want to create a class-level var:
var hidden = [String]()
and "re-set" it to "xxxxxx..." when you start a new game.
Then, with each guess, you'll be keeping the old letters and replacing new letters.
Suppose the game word is EASY:
hidden is "xxxx"
guessed letter is "A"
hidden goes from "xxxx" to "xAxx"
guessed letter is "Y"
hidden goes from "xAxx" to "xAxY"
guessed letter is "E"
hidden goes from "xAxY" to "EAxY"
and so on.
Here's a quick example you can run:
class HangmanViewController: UIViewController, UITextFieldDelegate {
var allWords: [String] = [
"easy",
"birthday",
"laboratory",
"hangman",
"iphone",
"requirements",
"gaming",
]
var usedLetters = [String]()
var startWords = [String]()
var promptWord = String()
var randomWord = ""
var hidden = [String]()
let tf = UITextField()
let progressLabel = UILabel()
let usedLabel = UILabel()
let newGameButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
tf.textAlignment = .center
tf.borderStyle = .roundedRect
tf.textAlignment = .center
tf.font = .monospacedSystemFont(ofSize: 24.0, weight: .light)
tf.delegate = self
progressLabel.textAlignment = .center
progressLabel.font = .monospacedSystemFont(ofSize: 24.0, weight: .light)
progressLabel.backgroundColor = .yellow
usedLabel.textAlignment = .center
usedLabel.font = .monospacedSystemFont(ofSize: 16.0, weight: .light)
usedLabel.backgroundColor = .cyan
newGameButton.setTitle("New Game", for: [])
newGameButton.setTitleColor(.white, for: .normal)
newGameButton.setTitleColor(.lightGray, for: .highlighted)
newGameButton.backgroundColor = .systemBlue
newGameButton.layer.cornerRadius = 8
newGameButton.layer.borderColor = UIColor.blue.cgColor
newGameButton.layer.borderWidth = 1
[tf, progressLabel, usedLabel, newGameButton].forEach { v in
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
tf.widthAnchor.constraint(equalToConstant: 80.0),
tf.topAnchor.constraint(equalTo: g.topAnchor, constant: 80.0),
tf.centerXAnchor.constraint(equalTo: g.centerXAnchor),
progressLabel.widthAnchor.constraint(equalToConstant: 300.0),
progressLabel.topAnchor.constraint(equalTo: tf.bottomAnchor, constant: 40.0),
progressLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
usedLabel.widthAnchor.constraint(equalToConstant: 300.0),
usedLabel.topAnchor.constraint(equalTo: progressLabel.bottomAnchor, constant: 40.0),
usedLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
newGameButton.widthAnchor.constraint(equalToConstant: 200.0),
newGameButton.topAnchor.constraint(equalTo: usedLabel.bottomAnchor, constant: 40.0),
newGameButton.centerXAnchor.constraint(equalTo: g.centerXAnchor),
])
newGameButton.addTarget(self, action: #selector(newGameTapped(_:)), for: .touchUpInside)
newGame()
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// upper-case the entered letter
let s = string.uppercased()
// we don't want to process if a string was pasted into the field
if s.count != 1 {
return false
}
// only allow A - Z
if s.rangeOfCharacter(from: .uppercaseLetters) == nil {
return false
}
// replace the current text
textField.text = s
// process the entered letter
submit(s)
// don't let the textfield process the string
return false
}
func newGame() {
// cycle the array of game words
allWords.append(allWords.removeFirst())
// safely unwrap
guard let w = allWords.first else {
fatalError("Bad setup")
}
// upper-case the word
randomWord = w.uppercased()
// set hidden string to "####..."
hidden = Array(repeating: "#", count: randomWord.count)
// clear used letters
usedLetters = []
// update the game progress label
progressLabel.text = String(hidden.joined())
progressLabel.textColor = .black
// update used letters label
usedLabel.text = " "
// hide the new game button
newGameButton.isHidden = true
// re-enable text field
tf.isUserInteractionEnabled = true
// for development
print("New Game Word is:", randomWord)
}
func submit(_ answer: String) {
if usedLetters.contains(answer) {
return
}
usedLetters.append(answer)
usedLabel.text = usedLetters.joined()
let wordArray = randomWord.map(String.init)
for index in hidden.indices {
if wordArray[index] == answer {
hidden[index] = wordArray[index]
}
}
progressLabel.text = hidden.joined()
if hidden == wordArray {
progressLabel.textColor = .red
// clear and disable text field
tf.text = ""
tf.isUserInteractionEnabled = false
// dismiss keyboard
view.endEditing(true)
// show the new game button
newGameButton.isHidden = false
}
}
#objc func newGameTapped(_ sender: UIButton) {
newGame()
}
}
It will look about like this (the cyan label will show the used letters). I'll type T, R, Y, S, M, E, A:

Countdown timer in table view cell shows different values after scrolling

The problem is described in title, but to be more specific here is a full picture.
I have a custom table view cell subclass with label inside it displaying the countdown timer. When there a small portion of timers it works fine, but with a lot of data I need to display timers far beyond the visible cells and when I scroll down fast and then scroll up fast, the timer values in cells start to show different values until a certain point in time, after which it shows the right value.
I tried different variants for those reuseable cells, but I can’t spot a problem. Help needed!!!
Here is the code of implementation of logic.
Custom cell subclass:
let calendar = Calendar.current
var timer: Timer?
var deadlineDate: Date? {
didSet {
updateTimeLabel()
}
}
override func awakeFromNib() {
purchaseCellCardView.layer.cornerRadius = 10
let selectedView = UIView(frame: CGRect.zero)
selectedView.backgroundColor = UIColor.clear
selectedBackgroundView = selectedView
}
override func prepareForReuse() {
super.prepareForReuse()
if timer != nil {
print("Invalidated!")
timer?.invalidate()
timer = nil
}
}
func configure(for purchase: Purchase) {
purchaseSubjectLabel.text = purchase.subject
startingPriceLabel.text = purchase.NMC
stageLabel.text = purchase.stage
fzImageView.image = purchase.fedLaw.contains("44") ? #imageLiteral(resourceName: "FZ44") : #imageLiteral(resourceName: "FZ223")
timeLabel.isHidden = purchase.stage == "Работа комиссии"
warningImageView.image = purchase.warningImage
}
func updateTimeLabel() {
setTimeLeft()
timer = Timer(timeInterval: 1, repeats: true) { [weak self] _ in
guard let strongSelf = self else {return}
strongSelf.setTimeLeft()
}
RunLoop.current.add(timer!, forMode: .commonModes)
}
#objc private func setTimeLeft() {
let currentDate = getCurrentLocalDate()
if deadlineDate?.compare(currentDate) == .orderedDescending {
var components = calendar.dateComponents([.day, .hour, .minute, .second], from: currentDate, to: deadlineDate!)
let dayText = (components.day! == 0 || components.day! < 0) ? "" : String(format: "%i", components.day!)
let hourText = (components.hour == 0 || components.hour! < 0) ? "" : String(format: "%i", components.hour!)
switch (dayText, hourText) {
case ("", ""):
timeLabel.text = String(format: "%02i", components.minute!) + ":" + String(format: "%02i", components.second!)
case ("", _):
timeLabel.text = hourText + " ч."
default:
timeLabel.text = dayText + " дн."
}
} else {
stageLabel.text = "Работа комиссии"
timeLabel.text = ""
timeLabel.isHidden = true
timer?.invalidate()
}
}
private func getCurrentLocalDate() -> Date {
var now = Date()
var nowComponents = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: now)
nowComponents.timeZone = TimeZone(abbreviation: "UTC")
now = calendar.date(from: nowComponents)!
return now
}
deinit {
print("DESTROYED")
timer?.invalidate()
timer = nil
}
The most important part of tableView(_cellForRowAt:)
case .results:
if filteredArrayOfPurchases.isEmpty {
let cell = tableView.dequeueReusableCell(
withIdentifier: TableViewCellIdentifiers.nothingFoundCell,
for: indexPath)
let label = cell.viewWithTag(110) as! UILabel
switch segmentedControl.index {
case 1:
label.text = "Нет закупок способом\n«Запрос предложений»"
case 2:
label.text = "Нет закупок способом\n«Конкурс»"
case 3:
label.text = "Нет закупок способом\n«Аукцион»"
default:
label.text = "Нет закупок способом\n«Запрос котировок»"
}
return cell
} else {
let cell = tableView.dequeueReusableCell(
withIdentifier: TableViewCellIdentifiers.purchaseCell,
for: indexPath) as! PurchaseCell
cell.containerViewTopConstraint.constant = indexPath.row == 0 ? 8.0 : 4.0
cell.containerViewBottomConstraint.constant = indexPath.row == filteredArrayOfPurchases.count - 1 ? 8.0 : 4.0
let purchase = filteredArrayOfPurchases[indexPath.row]
cell.configure(for: purchase)
if cell.timer != nil {
cell.updateTimeLabel()
} else {
search.getDeadlineDateAndTimeToApply(purchase.purchaseURL, purchase.fedLaw, purchase.stage, completion: { (date) in
cell.deadlineDate = date
})
}
return cell
}
And the last piece of a puzzle:
func getDeadlineDateAndTimeToApply(_ url: URL?, _ fedLaw: String, _ stage: String, completion: #escaping (Date) -> ()) {
var deadlineDateAndTimeToApply = Date()
guard stage != "Работа комиссии" else { return }
if let url = url {
dataTask = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
if let error = error as NSError?, error.code == -403 {
// TODO: Add alert here
return
}
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200, let data = data, let html = String(data: data, encoding: .utf8), let purchasePageBody = try? SwiftSoup.parse(html), let purchaseCard = try? purchasePageBody.select("td").array() else {return}
let mappedArray = purchaseCard.map(){String(describing: $0)}
if fedLaw.contains("44") {
guard let deadlineDateToApplyString = try? purchaseCard[(mappedArray.index(of: "<td class=\"fontBoldTextTd\">Дата и время окончания подачи заявок</td>"))! + 1].text().components(separatedBy: " ") else {return}
dateFormatter.dateFormat = "dd.MM.yyyy HH:mm"
let deadlineDateToApply = deadlineDateToApplyString.first!
let deadlineTimeToApply = deadlineDateToApplyString[1]
guard let deadlineDateAndTimeToApplyCandidate = dateFormatter.date(from: "\(deadlineDateToApply) \(deadlineTimeToApply)") else {return}
deadlineDateAndTimeToApply = deadlineDateAndTimeToApplyCandidate
} else {
guard let deadlineDateToApplyString = try? purchaseCard[(mappedArray.index(of: "<td>Дата и время окончания подачи заявок<br> (по местному времени заказчика)</td>"))! + 1].text().components(separatedBy: " ") else {return}
dateFormatter.dateFormat = "dd.MM.yyyy HH:mm"
let deadlineDateToApply = deadlineDateToApplyString.first!
let deadlineTimeToApply = deadlineDateToApplyString[2]
guard let deadlineDateAndTimeToApplyCandidate = dateFormatter.date(from: "\(deadlineDateToApply) \(deadlineTimeToApply)") else {return}
deadlineDateAndTimeToApply = deadlineDateAndTimeToApplyCandidate
}
DispatchQueue.main.async {
completion(deadlineDateAndTimeToApply)
}
})
dataTask?.resume()
}
}
A few notes:
Tried resetting deadlineDate to nil in prepareForReuse() - doesn’t help;
Using SwiftSoup Framework to parse HTML as you can see in the last code example if it matters.
This is quite a lot of code but from what you are describing your issue is in reusing cells.
You would do well to separate the timers out of the cells and put them inside your objects. It is where they belong (or in some manager like view controller). Imagine having something like the following:
class MyObject {
var timeLeft: TimeInterval = 0.0 {
didSet {
if timeLeft > 0.0 && timer == nil {
timer = Timer.scheduled...
} else if timeLeft <= 0.0, let timer = timer {
timer.invalidate()
self.timer = nil
}
delegate?.myObject(self, updatedTimeLeft: timeLeft)
}
}
weak var delegate: MyObjectDelegate?
private var timer: Timer?
}
Now all you need is is a cell for row at index path to assign your object: cell.myObject = myObjects[indexPath.row].
And your cell would do something like:
var myObject: MyObject? {
didSet {
if oldValue.delegate == self {
oldValue.delegate = nil // detach from previous item
}
myObject.delegate = self
refreshUI()
}
}
func myObject(_ sender: MyObject, updatedTimeLeft timeLeft: TimeInterval) {
refreshUI()
}
I believe the rest should be pretty much straight forward...
Your problem is here:
search.getDeadlineDateAndTimeToApply(purchase.purchaseURL,
purchase.fedLaw,
purchase.stage,
completion: { (date) in
cell.deadlineDate = date
})
getDeadlineDateAndTimeToApply runs asynchronously, calculates something, and then updates the cell.deadlineData in the main thread (which is fine). But in the meantime, while calculating something, the user might have scrolled up and down, the cell might have been reused for another row, and now the update updates the cell incorrectly.
What you need to do is: Do not store the UITableViewCell directly. Instead, keep track of the IndexPath to be updated, and once the caluclation is done, retrieve the the cell that belongs to that IndexPath and update this.

How to detect if UI Label was tapped?

I'm trying to make a choose-your-own adventure game that changes the text of two labels (the user choices) depending on which label the user taps. I figured I would just do a very nested if-else statement rather than bother with trying to implement a binary tree. I know how to attach the gesture recognizer to a label (I think):
let tapped1 = UITapGestureRecognizer(target: self, action: #selector(VCGame.usrChose1))
choice1.isUserInteractionEnabled = true
choice1.addGestureRecognizer(tapped1)
let tapped2 = UITapGestureRecognizer(target: self, action: #selector(VCGame.usrChose2))
choice2.isUserInteractionEnabled = true
choice2.addGestureRecognizer(tapped2)
and I can define what to do when the label is touched in the usrChose1 and usrChose2 functions, however, those functions only work once: the first time the function is chosen and my game has more than just one choice. From there, the labels will just do the same thing if the user touches them.
How would I go about having a condition inside the if-else statement that evaluates to true or false if label1 or label2 is tapped?
Here's the code for usrChoice1 and usrChoice2, for clarification
func usrChose1(sender : UITapGestureRecognizer) {
print("tap 1 working")
choice1.text = "choice1.1"
choice2.text = "choice1.2"
}
func usrChose2(sender : UITapGestureRecognizer) {
print("tap2 working")
choice1.text = "update2.1";
choice2.text = "update2.2"
}
Below image shows my requirement :
According to your requirement, I have tried the following:
I have made a dummy project with two labels inside a view controller
ViewController.swift
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var choice1Label: UILabel!
#IBOutlet weak var choiceLabel2: UILabel!
var tapStart: Bool = false
var levelType1: Level?
var levelType2: Level?
override func viewDidLoad() {
super.viewDidLoad()
let tapped1 = UITapGestureRecognizer(target: self, action: #selector(usrChose1))
choice1Label.isUserInteractionEnabled = true
choice1Label.addGestureRecognizer(tapped1)
let tapped2 = UITapGestureRecognizer(target: self, action: #selector(usrChose2))
choiceLabel2.isUserInteractionEnabled = true
choiceLabel2.addGestureRecognizer(tapped2)
setup()
}
var currentLevel1: Level?
var currentLevel2: Level?
func setup() {
let lb2Child1Child1 = Level(text: "2.1.1", subLevels: nil)
let lb2Child1Child2 = Level(text: "2.1.2", subLevels: nil)
let lb1Child1Child1 = Level(text: "1.1.1", subLevels: nil)
let lb1Child1Child2 = Level(text: "1.1.2", subLevels: nil)
let lb1Child2Child1 = Level(text: "1.2.1", subLevels: nil)
let lb1Child2Child2 = Level(text: "1.2.2", subLevels: nil)
let lb1Child1 = Level(text: "1.1", subLevels: [lb1Child1Child1, lb1Child1Child2])
let lb1Child2 = Level(text: "1.2", subLevels: [lb1Child2Child1, lb1Child2Child2])
let lb2Child1 = Level(text: "2.1", subLevels: [lb2Child1Child1, lb2Child1Child2])
let lb2Child2 = Level(text: "2.2", subLevels: nil)
levelType1 = Level(text: "1", subLevels: [lb1Child1, lb1Child2])
levelType2 = Level(text: "2", subLevels: [lb2Child1, lb2Child2])
choice1Label.text = levelType1!.text ?? ""
choiceLabel2.text = levelType2!.text ?? ""
}
func usrChose1(sender : UITapGestureRecognizer) {
if !tapStart {
currentLevel1 = levelType1
tapStart = true
}
if let subLevelsArray = currentLevel1?.subLevels {
print(subLevelsArray[0].text ?? "")
print(subLevelsArray[1].text ?? "")
choice1Label.text = subLevelsArray[0].text ?? ""
choiceLabel2.text = subLevelsArray[1].text ?? ""
currentLevel1 = subLevelsArray[0]
currentLevel2 = subLevelsArray[1]
}
}
func usrChose2(sender : UITapGestureRecognizer) {
//print("tap2 working")
// choice1Label.text = "update2.1";
//choiceLabel2.text = "update2.2"
if !tapStart {
currentLevel2 = levelType2
tapStart = true
}
if let subLevelsArray = currentLevel2?.subLevels {
print(subLevelsArray[0].text ?? "")
print(subLevelsArray[1].text ?? "")
choice1Label.text = subLevelsArray[0].text ?? ""
choiceLabel2.text = subLevelsArray[1].text ?? ""
currentLevel1 = subLevelsArray[0]
currentLevel2 = subLevelsArray[1]
}
}
}
I have made a class named Level to represent a single level and each level contains sublevels
Level.swift
import UIKit
class Level {
var text: String?
var subLevels: [Level]?
init(text: String, subLevels: [Level]?) {
self.text = text
self.subLevels = subLevels ?? nil
}
}
You have to add UITapGestureRecognizer in UILabel or UIView whatever is container.
Add 2 different Int variables in each functions usrChose1 and usrChose2 respectively, which will be work as a counter.
var i = 0
var j = 0
func usrChose1(_ recognizer: UITapGestureRecognizer) {
i++
print("Total clicked label 1 :::",i)
}
func usrChose2(_ recognizer: UITapGestureRecognizer) {
j++
print("Total clicked label 2 :::",j)
}

Swift - Animate dynamically created UIImageView

Initially I had this code working when I was just animating the one UIImageView that I had. But then I changed it to animate several dynamically created UIImageViews, however since they are dynamically created inside a for loop, I'm finding it difficult to animate them as I did the initial one.
override func viewDidLoad() {
super.viewDidLoad()
var sprite: UIImage = UIImage(named: "sprites/areaLocatorSprite.png")!
var locations:NSArray = animal[eventData]["locations"] as NSArray
for var i = 0; i < locations.count; i++ {
println(locations[i]["locationx"])
var locationx = locations[i]["locationx"] as String
var locationy = locations[i]["locationy"] as String
let x = NSNumberFormatter().numberFromString(locationx)
let y = NSNumberFormatter().numberFromString(locationy)
let cgfloatx = CGFloat(x!)
let cgfloaty = CGFloat(y!)
var mapSprite: UIImageView
mapSprite = UIImageView(image: sprite)
mapSprite.frame = CGRectMake(cgfloatx,cgfloaty,10,10)
townMap.addSubview(mapSprite)
timer = NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: Selector("flash"), userInfo: nil, repeats: true)
}
}
func flash() {
var mapSprite:UIImageView?
if mapSprite?.alpha == 1 {
mapSprite?.alpha = 0
} else {
mapSprite?.alpha = 1
}
}
This does not work as the mapSprite in the flash function is different to the one in the for loop. How can I refer to the one in the for loop and then animate it? Or would there be a better alternative to what I'm currently doing?
Many thanks!
EDIT
Using Xcode 6.2
You need to store the views into a property and then enumerate that property each time your timer event is fired
var sprites: [UIImageView]?
override func viewDidLoad() {
super.viewDidLoad()
var sprite = UIImage(named: "sprites/areaLocatorSprite.png")!
var locations:NSArray = animal[eventData]["locations"] as NSArray
self.sprites = map(locations) {
var locationx = $0["locationx"] as String
var locationy = $0["locationy"] as String
let x = NSNumberFormatter().numberFromString(locationx)
let y = NSNumberFormatter().numberFromString(locationy)
let cgfloatx = CGFloat(x!)
let cgfloaty = CGFloat(y!)
var mapSprite = UIImageView(image: sprite)
mapSprite.frame = CGRectMake(cgfloatx,cgfloaty,10,10)
townMap.addSubview(mapSprite)
return mapSprite
}
timer = NSTimer.scheduledTimerWithTimeInterval(0.35, target: self, selector: Selector("flash"), userInfo: nil, repeats: true)
}
func flash() {
if let sprites = self.sprites {
for sprite in sprites {
sprite.alpha = sprite.alpha == 0 ? 1 : 0
}
}
}

Memory leak issue iOS NSCFString

Original code using join() shows a memory leak in Instruments. I used a loop instead and it seems to have fixed the issue. Curious if this seems to be a framework issue?
//in ViewDidLoad
let saveButton = UIBarButtonItem(barButtonSystemItem: .Save, target: self, action: "saveBtnAc")
self.navigationItem.rightBarButtonItem = saveButton
//end ViewDidLoad
//using a loop to join the string instead of join()
func doJndString(theArray: [String]) -> String {
var updatedAryJnd = String()
var count = 0
let lastItemCount = theArray.count - 1
while (count < lastItemCount) {
updatedAryJnd += (theArray[count] + "§")
count++
}
updatedAryJnd += theArray[lastItemCount]
return updatedAryJnd
}
//button action
func saveBtnAc() {
var myAry = [""]
if let items1 = setupItem?.valueForKey("itemsAry1") as? String {
myAry = items1.componentsSeparatedByString("§")
}
myAry[itemClkdVar] = myTxtOutlet.text
//I get a leak when using the following join()
//let updatedAryJnd = "§".join(myAry)
//setupItem!.setValue(updatedAryJnd, forKey: "itemsAry1")
//change above to this and no leak
setupItem!.setValue(doJndString(myAry), forKey: "itemsAry1")
var error: NSError? = nil
if !contextOb!.save(&error) {
println("had an error")
}
}

Resources