I am just learning Swift and I am following along with another project that I had worked on. However, I am getting this error:
Error I am getting:
Thread 1: Fatal error: Index out of range
All my code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var scoreLabel : SKLabelNode?
var player : SKSpriteNode?
var track : Int = 0
var trackArray : [SKSpriteNode]? = [SKSpriteNode]()
var ballDirection = Int(arc4random_uniform(2))
var velocityArray = [Int]()
var playerVelocity : Int = GKRandomSource.sharedRandom().nextInt(upperBound: 100)
var currentScore : Int = 0 {
didSet {
self.scoreLabel?.text = "SCORE: \(self.currentScore)"
}
}
func createHUD() {
scoreLabel = self.childNode(withName: "score") as? SKLabelNode
currentScore = 0
}
//I think the problem is coming from this function, however I more-or-less copied it from a tutorial project that works so I don't know what the problem is.
func setupTracks() {
for i in {
if let track = self.childNode(withName: "\(i)") as? SKSpriteNode {
trackArray?.append(track)
print(track)
}
}
}
func createBall() {
player = SKSpriteNode(imageNamed: "ball1")
player?.physicsBody = SKPhysicsBody(circleOfRadius: player!.size.width / 2)
player?.physicsBody?.linearDamping = 0
//This line throws the error
let ballPosition = trackArray?[track].position
print(track)
player?.position = CGPoint(x: (ballPosition?.x)!, y: (ballPosition?.y)!)
player?.position.x = (ballPosition!.x)
player?.position.y = (ballPosition!.y)
self.addChild(player!)
}
override func didMove(to view: SKView) {
createHUD()
createBall()
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I have 8 Color Sprite objects that I put in through the GameScene.sks each named 0 - 9,
I think it is looking for a value in the array that has not been yet assigned? but I have a part that (I think) is assigning that value. It also works in the other project so I am very lost.
Some of this doesn't make 100% sense to me without context, but based on what I can infer it seems a better solution for you would be to use a single dictionary to access the SKSpriteNode as well as the direction and velocity. To achieve this you could create a custom object, or just use a simple tuple.
After you initialize the dictionary and fill it with the objects you can access a single value in the dictionary to get all of the data you need for your createEnemy function. It might look something like this:
var tracks = [Int : (node: SKSpriteNode, direction: CGFloat, velocity: CGFloat)]()
func setupTrack() {
for i in 0 ... 9 {
if let track = self.childNode(withName: "\(i)") as? SKSpriteNode {
//Get direction and velocity here, if initial value is known
//Setting to 0 for now
tracks[i] = (node: track, direction: 0, velocity: 0)
}
}
}
func createEnemy(forTrack track: Int) -> SKShapeNode? {
guard let thisTrack = tracks[track] else { return nil }
let enemySprite = SKShapeNode()
enemySprite.name = "ENEMY"
enemySprite.path = CGPath(roundedRect: CGRect(x: 0, y: -10, width: 20, height: 20), cornerWidth: 10, cornerHeight: 10, transform: nil)
let enemyPosition = thisTrack.node.position
let left = thisTrack.direction
enemySprite.position.x = left ? -50 : self.size.height + 50
enemySprite.position.y = enemyPosition.y
enemySprite.physicsBody?.velocity = left ? CGVector(dx: thisTrack.velocity, dy: 0) : CGVector(dx: 0, dy: -thisTrack.velocity)
return enemySprite
}
This will prevent you from going out of bounds on your array. Note on this: Since you are looping through a hardcoded 0...9 range, but you are optionally appending items to that Array (in the case where your if let track fails) you have no guarantee that the "track" item with the name "7" corresponds to the 7th index in your array. This is another reason a Dictionary is safer. Additionally, it seems you are maintaining 3 separate arrays (tracks, directionArray, and velocityArray), which seems prone to error for the same reason I just described.
Apple have given an example of a traditional pop and pull stack in their programming guide:
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
My intention was to create a structure from which to calculate a "moving average" of the contents while adding extra values during my code - averaging over the last 10 items I've added to the stack.
Can the above be modified to do this, or am I better using a new structure that looks something like the below:
struct Avg<Element> {
var items = [Element]()
mutating func additem(_item: Element) {
if items.count<10 {
items.append(item)
} else {
for i in (1...10).reversed() {
items[i] = items[i-1]
}
items[0]=item
}
}
// more functions
}
items.remove(at: 0) or items.removeFirst() would be more efficient
than your loop to remove the first array element.
But what I would actually do is to use the internal array as a "ring buffer" and overwrite
elements once the window size has been reached, instead of moving
all elements. Something like this:
struct Avg<Element> {
let windowSize: Int
var totalCount: Int
var items: [Element]
init(windowSize: Int) {
self.windowSize = windowSize
self.totalCount = 0
self.items = []
}
mutating func additem(_ newItem: Element) {
if items.count < windowSize {
items.append(newItem)
} else {
items[totalCount % windowSize] = newItem
}
totalCount += 1
}
// more functions
}
I made table view with friends contact info.
And Each cell has button if touched,
I want to insert the info to selected friend array
(by the array, I made another small view to slide up with the friends list).
But If user the button one more,
I want to delete the friend Info in the selected friend array.
I know how to append to array,
but I don't know how to erase the specific item(NSObject) in array
by not using index.
my source code is below
class FriendModel : NSObject {
dynamic var index : ""
dynamic var thumbnail : ""
dynamic var name : ""
}
and In view controller class,
var selectedList = [FriendModel]()
#IBAction func SelectAct(_ sender: Any) {
let chooseBtn = sender as! UIButton
let indexPath = NSIndexPath(row: chooseBtn.tag, section:0)
let cell = tableView.cellForRow(at: indexPath as IndexPath) as! FriendsListSendCell
// when selected button is pushed
if chooseBtn.isSelected == true {
chooseBtn.isSelected = false
count = count! - 1
if self.count! < 1 {
self.windowShowUp = false
UIView.animate(withDuration: 0.1, delay: 0.1, options: [], animations:{self.selectedBoard.center.y += 80 }, completion: nil)
self.checkNumLabel.text = ""
}else{
}
//////////////////////////here////////////////////////////
//////////////////how to erase the FriendModel(NSObject) in selectedList.
}
//when the unselected button is pushed
else {
//instance for append the friend info
let friendInfo = FriendModel()
chooseBtn.isSelected = true
count = count! + 1
friendInfo.thumbnail = cell.thumbnail
friendInfo.name = cell.nameLabel.text!
//add friend info to selectedList
self.selectedList.append(friendInfo)
print("\(self.selectedList)")
if self.windowShowUp! == false{
self.windowShowUp = true
UIView.animate(withDuration: 0.1, delay: 0.1, options: [], animations:{self.selectedBoard.center.y -= 80 }, completion: nil)
}else{
}
}
}
You can use index(where:) to get index of your object and then remove item at index position.
class FriendModel {
var index = ""
var thumbnail = ""
var name = ""
}
let fm0 = FriendModel()
fm0.index = "100"
fm0.thumbnail = "tell.png"
fm0.name = "Phillips"
let fm1 = FriendModel()
fm1.index = "200"
fm1.thumbnail = "ask.png"
fm1.name = "Allen"
var array = [FriendModel]()
array.append(fm0)
array.append(fm1)
// The index below is an index of the array. Do not confuse with the FriendModel.index
let index = array.index {
return $0.thumbnail == "ask.png" && $0.name == "Allen"
}
array.forEach { print($0.name) }
print("Array index:", index ?? "n/a")
array.remove(at: index!)
array.forEach { print($0.name) }
You could use MirekE's solution which would work but here's an alternative solution.
First step would be identifying what the unique identifier on your FriendModel object is such as an id property.
Then on your model, since it is an NSObject, override the isEqual function like this:
override public func isEqual(object: AnyObject?) -> Bool {
let friend = object as? FriendModel
if self.id == friend?.id { return true }
return false
}
At this point you can use your desired form of iteration to find your element in the array and use remove.
I'm having a bit trouble saving an array of strings to userDefaults. I have an Array of strings declaired in a class, with a property observer ment to sync to userDefaults. Furthermore, I want the array to be limited to a maximum of 5 Strings.
let userDefaults = NSUserDefaults.standardUserDefaults()
var suggestions: [String]! {
didSet {
var arrayEnd = suggestions.count
if arrayEnd >= 5 {
arrayEnd = 4
}
let newArray = Array(suggestions[0...arrayEnd])
userDefaults.setObject(newArray, forKey: "suggestions")
userDefaults.synchronize()
}
}
func getSuggestionData() {
if userDefaults.valueForKey("suggestions") != nil {
self.suggestions = userDefaults.valueForKey("suggestions") as? [String]
}
}
override func viewDidLoad() {
super.viewDidLoad()
getSuggestionData()
suggestions.insert("someString", atIndex: 0)
}
}
When i run this i get:
fatal error: unexpectedly found nil while unwrapping an Optional value
on the line where I try to insert a new object to the array.
I have tried following the approach in this thread, but it didn't save anything to the list.
I'm new to swift, and optional-types aren't my strong side, maybe someone know what's wrong?
As reading from user defaults could return nil values, use optional binding to be safe:
func getSuggestionData() {
if let suggestionArray = userDefaults.objectForKey("suggestions") {
self.suggestions = suggestionArray
} else {
self.suggestions = [String]()
}
}
But I'd recommend to use an non-optional variable with a defined default value.
In AppDelegate of your app, override init() or insert the code to register the key/value pair.
override init()
{
let defaults = NSUserDefaults.standardUserDefaults()
let defaultValue = ["suggestions" : [String]()];
defaults.registerDefaults(defaultValue)
super.init()
}
Registering the default keys and values is the way Apple suggests in the documentation.
If no value is written yet, the default value (empty array) is read.
If there is a value, the actual value is read
Instead of the value observer of the variable suggestions implement a method to insert a new value, delete the last one and write the array to disk.
There is no need to use optional binding because the array in user defaults has always a defined state.
let userDefaults = NSUserDefaults.standardUserDefaults()
var suggestions = [String]()
func insertSuggestion(suggestion : String)
{
if suggestions.count == 5 { suggestions.removeLast() }
suggestions.insert(suggestion, atIndex: 0)
userDefaults.setObject(suggestions, forKey: "suggestions")
userDefaults.synchronize()
}
func getSuggestionData() {
suggestions = userDefaults.objectForKey("suggestions") as! [String]
}
override func viewDidLoad() {
super.viewDidLoad()
getSuggestionData()
insertSuggestion("someString")
}
A side note:
never use valueForKey: rather than objectForKey: if you don't need explicitly the key-value-coding method.
Hey Just try like this way.
var suggestions: [String] = Array() {
didSet {
var arrayEnd = suggestions.count
if arrayEnd >= 5 {
arrayEnd = 4
}
if arrayEnd > 0
{
let newArray = Array(suggestions[0...(arrayEnd-1)])
userDefaults.setObject(newArray, forKey: "suggestions")
userDefaults.synchronize()
}
}
}
func getSuggestionData() {
if userDefaults.valueForKey("suggestions") != nil {
self.suggestions = userDefaults.objectForKey("suggestions") as! Array
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
getSuggestionData()
if self.suggestions.count > 4
{
var str = "\(self.suggestions.count)"
self.suggestions.insert(str, atIndex: self.suggestions.count)
}
}
Suppose I have an array and I want to pick one element at random.
What would be the simplest way to do this?
The obvious way would be array[random index]. But perhaps there is something like ruby's array.sample? Or if not could such a method be created by using an extension?
Swift 4.2 and above
The new recommended approach is a built-in method on the Collection protocol: randomElement(). It returns an optional to avoid the empty case I assumed against previously.
let array = ["Frodo", "Samwise", "Merry", "Pippin"]
print(array.randomElement()!) // Using ! knowing I have array.count > 0
If you don't create the array and aren't guaranteed count > 0, you should do something like:
if let randomElement = array.randomElement() {
print(randomElement)
}
Swift 4.1 and below
Just to answer your question, you can do this to achieve random array selection:
let array = ["Frodo", "Samwise", "Merry", "Pippin"]
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
print(array[randomIndex])
The castings are ugly, but I believe they're required unless someone else has another way.
Riffing on what Lucas said, you could create an extension to the Array class like this:
extension Array {
func randomItem() -> Element? {
if isEmpty { return nil }
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
For example:
let myArray = [1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16]
let myItem = myArray.randomItem() // Note: myItem is an Optional<Int>
Swift 4 version:
extension Collection where Index == Int {
/**
Picks a random element of the collection.
- returns: A random element of the collection.
*/
func randomElement() -> Iterator.Element? {
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
}
}
In Swift 2.2 this can be generalised so that we have:
UInt.random
UInt8.random
UInt16.random
UInt32.random
UInt64.random
UIntMax.random
// closed intervals:
(-3...3).random
(Int.min...Int.max).random
// and collections, which return optionals since they can be empty:
(1..<4).sample
[1,2,3].sample
"abc".characters.sample
["a": 1, "b": 2, "c": 3].sample
First, implementing static random property for UnsignedIntegerTypes:
import Darwin
func sizeof <T> (_: () -> T) -> Int { // sizeof return type without calling
return sizeof(T.self)
}
let ARC4Foot: Int = sizeof(arc4random)
extension UnsignedIntegerType {
static var max: Self { // sadly `max` is not required by the protocol
return ~0
}
static var random: Self {
let foot = sizeof(Self)
guard foot > ARC4Foot else {
return numericCast(arc4random() & numericCast(max))
}
var r = UIntMax(arc4random())
for i in 1..<(foot / ARC4Foot) {
r |= UIntMax(arc4random()) << UIntMax(8 * ARC4Foot * i)
}
return numericCast(r)
}
}
Then, for ClosedIntervals with UnsignedIntegerType bounds:
extension ClosedInterval where Bound : UnsignedIntegerType {
var random: Bound {
guard start > 0 || end < Bound.max else { return Bound.random }
return start + (Bound.random % (end - start + 1))
}
}
Then (a little more involved), for ClosedIntervals with SignedIntegerType bounds (using helper methods described further below):
extension ClosedInterval where Bound : SignedIntegerType {
var random: Bound {
let foot = sizeof(Bound)
let distance = start.unsignedDistanceTo(end)
guard foot > 4 else { // optimisation: use UInt32.random if sufficient
let off: UInt32
if distance < numericCast(UInt32.max) {
off = UInt32.random % numericCast(distance + 1)
} else {
off = UInt32.random
}
return numericCast(start.toIntMax() + numericCast(off))
}
guard distance < UIntMax.max else {
return numericCast(IntMax(bitPattern: UIntMax.random))
}
let off = UIntMax.random % (distance + 1)
let x = (off + start.unsignedDistanceFromMin).plusMinIntMax
return numericCast(x)
}
}
... where unsignedDistanceTo, unsignedDistanceFromMin and plusMinIntMax helper methods can be implemented as follows:
extension SignedIntegerType {
func unsignedDistanceTo(other: Self) -> UIntMax {
let _self = self.toIntMax()
let other = other.toIntMax()
let (start, end) = _self < other ? (_self, other) : (other, _self)
if start == IntMax.min && end == IntMax.max {
return UIntMax.max
}
if start < 0 && end >= 0 {
let s = start == IntMax.min ? UIntMax(Int.max) + 1 : UIntMax(-start)
return s + UIntMax(end)
}
return UIntMax(end - start)
}
var unsignedDistanceFromMin: UIntMax {
return IntMax.min.unsignedDistanceTo(self.toIntMax())
}
}
extension UIntMax {
var plusMinIntMax: IntMax {
if self > UIntMax(IntMax.max) { return IntMax(self - UIntMax(IntMax.max) - 1) }
else { return IntMax.min + IntMax(self) }
}
}
Finally, for all collections where Index.Distance == Int:
extension CollectionType where Index.Distance == Int {
var sample: Generator.Element? {
if isEmpty { return nil }
let end = UInt(count) - 1
let add = (0...end).random
let idx = startIndex.advancedBy(Int(add))
return self[idx]
}
}
... which can be optimised a little for integer Ranges:
extension Range where Element : SignedIntegerType {
var sample: Element? {
guard startIndex < endIndex else { return nil }
let i: ClosedInterval = startIndex...endIndex.predecessor()
return i.random
}
}
extension Range where Element : UnsignedIntegerType {
var sample: Element? {
guard startIndex < endIndex else { return nil }
let i: ClosedInterval = startIndex...endIndex.predecessor()
return i.random
}
}
You can use Swift's built-in random() function as well for the extension:
extension Array {
func sample() -> Element {
let randomIndex = Int(rand()) % count
return self[randomIndex]
}
}
let array = [1, 2, 3, 4]
array.sample() // 2
array.sample() // 2
array.sample() // 3
array.sample() // 3
array.sample() // 1
array.sample() // 1
array.sample() // 3
array.sample() // 1
Another Swift 3 suggestion
private extension Array {
var randomElement: Element {
let index = Int(arc4random_uniform(UInt32(count)))
return self[index]
}
}
Following others answer but with Swift 2 support.
Swift 1.x
extension Array {
func sample() -> T {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
Swift 2.x
extension Array {
func sample() -> Element {
let index = Int(arc4random_uniform(UInt32(self.count)))
return self[index]
}
}
E.g.:
let arr = [2, 3, 5, 7, 9, 11, 13, 17, 19, 23, 29, 31]
let randomSample = arr.sample()
An alternative functional implementation with check for empty array.
func randomArrayItem<T>(array: [T]) -> T? {
if array.isEmpty { return nil }
let randomIndex = Int(arc4random_uniform(UInt32(array.count)))
return array[randomIndex]
}
randomArrayItem([1,2,3])
Here's an extension on Arrays with an empty array check for more safety:
extension Array {
func sample() -> Element? {
if self.isEmpty { return nil }
let randomInt = Int(arc4random_uniform(UInt32(self.count)))
return self[randomInt]
}
}
You can use it as simple as this:
let digits = Array(0...9)
digits.sample() // => 6
If you prefer a Framework that also has some more handy features then checkout HandySwift. You can add it to your project via Carthage then use it exactly like in the example above:
import HandySwift
let digits = Array(0...9)
digits.sample() // => 8
Additionally it also includes an option to get multiple random elements at once:
digits.sample(size: 3) // => [8, 0, 7]
Swift 3
import GameKit
func getRandomMessage() -> String {
let messages = ["one", "two", "three"]
let randomNumber = GKRandomSource.sharedRandom().nextInt(upperBound: messages.count)
return messages[randomNumber].description
}
Swift 3 - simple easy to use.
Create Array
var arrayOfColors = [UIColor.red, UIColor.yellow, UIColor.orange, UIColor.green]
Create Random Color
let randomColor = arc4random() % UInt32(arrayOfColors.count)
Set that color to your object
your item = arrayOfColors[Int(randomColor)]
Here is an example from a SpriteKit project updating a SKLabelNode with a random String:
let array = ["one","two","three","four","five"]
let randomNumber = arc4random() % UInt32(array.count)
let labelNode = SKLabelNode(text: array[Int(randomNumber)])
If you want to be able to get more than one random element out of your array with no duplicates, GameplayKit has you covered:
import GameplayKit
let array = ["one", "two", "three", "four"]
let shuffled = GKMersenneTwisterRandomSource.sharedRandom().arrayByShufflingObjects(in: array)
let firstRandom = shuffled[0]
let secondRandom = shuffled[1]
You have a couple choices for randomness, see GKRandomSource:
The GKARC4RandomSource class uses an algorithm similar to that employed in arc4random family of C functions. (However, instances of this class are independent from calls to the arc4random functions.)
The GKLinearCongruentialRandomSource class uses an algorithm that is faster, but less random, than the GKARC4RandomSource class. (Specifically, the low bits of generated numbers repeat more often than the high bits.) Use this source when performance is more important than robust unpredictability.
The GKMersenneTwisterRandomSource class uses an algorithm that is slower, but more random, than the GKARC4RandomSource class. Use this source when itβs important that your use of random numbers not show repeating patterns and performance is of less concern.
I find using GameKit's GKRandomSource.sharedRandom() works best for me.
import GameKit
let array = ["random1", "random2", "random3"]
func getRandomIndex() -> Int {
let randomNumber = GKRandomSource.sharedRandom().nextIntWithUpperBound(array.count)
return randomNumber
or you could return the object at the random index selected. Make sure the function returns a String first, and then return the index of the array.
return array[randomNumber]
Short and to the point.
There is a built-in method on Collection now:
let foods = ["π", "π", "π£", "π"]
let myDinner = foods.randomElement()
If you want to extract up to n random elements from a collection you can add an extension like this one:
extension Collection {
func randomElements(_ count: Int) -> [Element] {
var shuffledIterator = shuffled().makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}
And if you want them to be unique you can use a Set, but the elements of the collection must conform to the Hashable protocol:
extension Collection where Element: Hashable {
func randomUniqueElements(_ count: Int) -> [Element] {
var shuffledIterator = Set(shuffled()).makeIterator()
return (0..<count).compactMap { _ in shuffledIterator.next() }
}
}
Latest swift3 code try it its working fine
let imagesArray = ["image1.png","image2.png","image3.png","image4.png"]
var randomNum: UInt32 = 0
randomNum = arc4random_uniform(UInt32(imagesArray.count))
wheelBackgroundImageView.image = UIImage(named: imagesArray[Int(randomNum)])
I figured out a very different way to do so using the new features introduced in Swift 4.2.
// ππΌ - 1
public func shufflePrintArray(ArrayOfStrings: [String]) -> String {
// - 2
let strings = ArrayOfStrings
//- 3
var stringans = strings.shuffled()
// - 4
var countS = Int.random(in: 0..<strings.count)
// - 5
return stringans[countS]
}
we declared a function with parameters taking an array of Strings and returning a String.
Then we take the ArrayOfStrings in a variable.
Then we call the shuffled function and store that in a variable. (Only supported in 4.2)
Then we declare a variable which saves a shuffled value of total count of the String.
Lastly we return the shuffled string at the index value of countS.
It is basically shuffling the array of strings and then also have a random pick of number of the total number of count and then returning the random index of the shuffled array.