use #objc func to place a empty array of imageViews - arrays

I want to use my swift code to place a imageview everytime the func moveRight is called. I want each new imageView to be separated by a 50 on the y axis. Right now my code compiles but nothing is changing when the function is called. But in the debug area I am seeing the count increasing.
import UIKit
class ViewController: UIViewController {
var myArray = [UIImageView]()
var bt = UIButton()
var count : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(bt)
bt.backgroundColor = UIColor.systemOrange
bt.frame = CGRect(x: view.center.x - 0, y: view.center.y , width: 50, height: 50)
bt.addTarget(self, action: #selector(moveRight), for: .touchUpInside)
}
#objc func moveRight() {
print("Yes")
myArray.forEach({
$0.backgroundColor = UIColor.systemTeal
self.view.addSubview($0)
})
var ht = 50
myArray.insert(UIImageView(), at: count)
print("Your Count is ", count)
myArray[count].frame = CGRect(x: view.center.x - 0, y: view.center.y + CGFloat(ht), width: 50, height: 50)
count += 1
ht += 50
}
}

You need to move the ht variable outside the moveRight() method.
Currently every time you run the method you recreate the variable and set it's initial value to 50, and then use that to set your location.
You need to do
class ViewController: UIViewController {
var myArray = [UIImageView]()
var bt = UIButton()
var count : Int = 0
var ht = 50 //initialise it here, then update it in the moveRight() method
//etc
and then remove the equivalent line from the method.

Related

Swift: Create multiple views through iteration with spacing without using StackView

I'm trying to create multiple labels with padding of 20 between each item without using stackView. My current logic has them all aligned on top of each other. Any thoughts whats wrong with the current code? Im not seeing it.
let sections = ["List", "Items", "News"]
var previousLabel: UILabel?
var sectionsArray = [UILabel]()
var leadConstraint: NSLayoutConstraint?
let navigationView = UIView(frame: CGRect(x: 20, y: 100, width: 100, height: 30))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
view.addSubview(navigationView)
navigationView.backgroundColor = .red
for label in sections {
let newLabel = UILabel(frame: CGRect.zero)
newLabel.text = label
newLabel.textColor = .white
newLabel.backgroundColor = .systemBlue
newLabel.sizeToFit()
navigationView.addSubview(newLabel)
if let previous = previousLabel {
leadConstraint = newLabel.leftAnchor.constraint(equalTo: previous.rightAnchor, constant: 20)
leadConstraint?.isActive = true
newLabel.topAnchor.constraint(equalTo: navigationView.topAnchor, constant: 0).isActive = true
newLabel.bottomAnchor.constraint(equalTo: navigationView.bottomAnchor, constant: 0).isActive = true
} else {
//1st item in section
newLabel.leftAnchor.constraint(equalTo: navigationView.leftAnchor, constant: 20).isActive = true
newLabel.topAnchor.constraint(equalTo: navigationView.topAnchor, constant: 0).isActive = true
newLabel.bottomAnchor.constraint(equalTo: navigationView.bottomAnchor, constant: 0).isActive = true
}
sectionsArray.append(newLabel)
previousLabel = newLabel
}
This is a image of the error message, i couldnt make out what exactly what constraint was having a problem.
Change
let newLabel = UILabel(frame: CGRect.zero)
To
let newLabel = UILabel(frame: CGRect.zero)
newLabel.translatesAutoresizingMaskIntoConstraints = false
This will expose other flaws in your implementation, but at least the labels will line up horizontally in the way you expect.

Global Array not Appending Swift

I have a global array called cannonArray that I initialized before any function. I have a touchesBegan function that appends values to the array. When I print the array from within the touchesBegan function, it prints perfectly. However, when I print the array from the didMoveToViewFunction, nothing updates and it simply prints [] (and doesn't change as it should because it is global). I also have another function called createEnemies that is on a timer and it has the same problem (prints proper appended array within the function, but not in another one). Thanks! :)
var cannonArray = [SKSpriteNode]()
var enemyArray = [SKSpriteNode]()
override func didMove(to view: SKView) {
enemyCreationTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(createEnemies), userInfo: nil, repeats: true)
print(enemyArray) //prints empty array
print(cannonArray) // prints empty array
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
cannon = SKSpriteNode(imageNamed: "cannon")
cannon.size = CGSize(width: 50, height: 50)
cannon.position = location
if frameArray[0].contains(location) || frameArray[1].contains(location) {
print("cant")
}else {
self.addChild(cannon)
self.cannonArray.append(cannon)
//if i print here it works
}
}
#objc func createEnemies() {
enemiesSpawned += 1
if enemiesSpawned > enemyNumber {
enemyCreationTimer.invalidate()
}else {
enemy = SKSpriteNode(imageNamed: "enemy")
enemy.position = CGPoint(x: spriteOne.position.x, y: CGFloat(650))
enemy.size = CGSize(width: 25, height: 25)
self.addChild(enemy)
enemy.zPosition = 0
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 12.5)
enemy.physicsBody?.affectedByGravity = false
enemy.run(moveEnemies())
enemyArray.append(enemy)
//if i print here it works
}
}

Generate imageView based off array.count - Swift

I am trying to generate a section of image views which will sit at the top of an application and update as the user progresses through the quiz.
My array variable is Qs, the code I have to generate an imageView is as follows:
var imageView: UIImageView!
var i = 0
var total = Int(Qs.capacity) // Just testing what .capacity does.
for i in 0..<(Qs.count-1){
imageView - UIImageView(frame: CGRect(x: 0, y: 75, width: 50, height: 50))
imageView.image = UIImage(named:"InfoIcon")
imageView.contentMode = .scaleAspectFit
self.view.addSubView(imageView)
}
I already have a variable which tracks the users progress through the quiz with just an integer if that would be any help, its declared along with the quiz functionality.
Here is a fantastically draw visual of what I am trying to accomplish:
Any help is appreciated,
thanks
Lets assume you have a container view attached at the top, then you can use this method to add any number of imageView's.
func addProgressImages(to view: UIView, count: Int) {
let screenSize = UIScreen.main.bounds.size
for i in 0..<count {
let spacing: CGFloat = 2
let tag = i + 888
let width: CGFloat = screenSize.width/CGFloat(count)
let height: CGFloat = width
if let view = view.viewWithTag(tag) {
view.removeFromSuperview()
}
let x = spacing + CGFloat(i) * width
let imageView = UIImageView(frame: CGRect(x: x, y: 100, width: width - spacing, height: height))
imageView.image = UIImage(named:"InfoIcon")
imageView.tag = tag
imageView.contentMode = .scaleAspectFit
view.addSubview(imageView)
}
}
You can play with y value for vertical alignment.

Swift- How to dynamically create labels

I have created some code which reads through an array and saves data for each index into variables which I then pass onto to a created label.
Below is the code:
example of data arr content :
["2,5","5,1"] two indexes inside array
for i in 0..<dataArr.count {
let element = dataArr[i]
let labelNum = UILabel()
label split = element.components(separatedBy: ",")
let num1 = split[0]
let num2 = split[1]
let num1Nnum2 = "number 1 :" + num1 + " number 2:" + num2
labelnum.text = num1Nnum2
labelnum.textAlignment = .center
labelnum.frame = CGRect( x:10, y:90, width:250, height: 80)
self.view.addSubview(labelnum)
}
how can I create it so that when the label is created the second time when it reads index[1] it creates a new label with same code but drop the label under the first label. I have tried to do :
labelnum[i] to attempt to create a new label using the value of index for example labelnum1 when i is = 1.
Any help will be Appreciated.
There is UIStackView in iOS which lets you add elements dynamically at the bottom or top of the existing views. You can always add a new label which automatically appears at the bottom of the view. You can also accomplish this with UITableView or UIScrollView.
Here is an example of UIStackView, dynamically appending new label below previous one. I hope you can infer this for your use case,
class ViewController: UIViewController {
var lastLabelCount = 0
var stackView: UIStackView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
let tap = UITapGestureRecognizer(target: self,
action: #selector(tapped))
view.addGestureRecognizer(tap)
createViews()
}
func createViews() {
stackView = UIStackView(frame: .zero)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .top
view.addSubview(stackView)
NSLayoutConstraint.activate([
stackView.leftAnchor.constraint(equalTo: view.leftAnchor),
stackView.rightAnchor.constraint(equalTo: view.rightAnchor),
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
])
}
#objc func tapped() {
let label = UILabel(frame: .zero)
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.black
label.text = "Hi I am label \(lastLabelCount)"
stackView.addArrangedSubview(label)
lastLabelCount += 1
}
}
Create a variable to hold Y Position of The Label. and in each iteration add the height of previous label in Y Position variable to drop new label to below previous one.
class ViewController: UIViewController {
let dataArr = ["2,5","5,1"]
override func viewDidLoad() {
super.viewDidLoad()
var yPos = 90
for i in 0..<dataArr.count {
let element = dataArr[i]
let labelNum = UILabel()
let split = element.components(separatedBy: ",")
let num1 = split[0]
let num2 = split[1]
let num1Nnum2 = "number 1 :" + num1 + " number 2:" + num2
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
self.view.addSubview(labelNum)
}
}

Swift 4 SprikeKit - Thread 1: Fatal error: Index out of range

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.

Resources