how to transfer part of characters below others in swift - arrays

enter image description here> I make the word game, the problem is this. When the word is big, the
cells come out of the screen, I need these cells to move below the
other cells and be centered. and the cells should have a fixed width =
20. that's all in my application Help implement.
p.s. I tried to add elements to the stackview, but the space between the words did not appear in the stackview
1. TileView.swift
import UIKit
protocol TileDragDelegateProtocol {
func tileView(tileView: TileView, didDragToPoint: CGPoint)
}
class TileView: UIImageView {
var letter: Character?
var isMatch = false
private var xOffset: CGFloat = 0.0
private var yOffset: CGFloat = 0.0
var dragDelegate: TileDragDelegateProtocol?
init(letter: Character, sideLength: CGFloat) {
self.letter = letter
//let scale = CGRect(x: 0, y: 0, width: self.bounds.size.width /* scale*/, height: self.bounds.size.height /** scale*/)
// super.init(frame: scale)
// let scale = CGRect(x: 0, y: 0, width: self.bounds.size.width /* scale*/, height: self.bounds.size.height /** scale*/)
let image = UIImage(named: "tile")
super.init(image: image)
let scale = sideLength / (image?.size.width)!
self.frame = CGRect(x: 0, y: 0, width: (image?.size.width)! * scale, height: (image?.size.height)! * scale)
// self.frame = CGRect(x: 0, y: 0, width: self.bounds.size.width /* scale*/, height: self.bounds.size.height /** scale*/)
//self.layer.cornerRadius = 23
//self.clipsToBounds = true
let letterLabel = UILabel(frame: self.bounds)
letterLabel.textAlignment = .center
letterLabel.textColor = .white
letterLabel.backgroundColor = .clear
letterLabel.text = String(letter).uppercased()
letterLabel.font = .boldSystemFont(ofSize: 40.0 / 3) //.systemFont(ofSize: 78.0 / 3/*scale*/)
//letterLabel.layer.cornerRadius = 23
//letterLabel.clipsToBounds = true
// self.backgroundColor = .red
// self.textAlignment = .center
// self.textColor = .white
// self.backgroundColor = .black
// self.text = String(letter).uppercased()
// self.font = .boldSystemFont(ofSize: 50.0 / 3)
self.addSubview(letterLabel)
self.isUserInteractionEnabled = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let point = touch.location(in: self.superview)
xOffset = point.x - self.center.x
yOffset = point.y - self.center.y
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let point = touch.location(in: self.superview)
self.center = CGPoint(x: point.x - xOffset, y: point.y - yOffset)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.touchesMoved(touches, with: event)
dragDelegate?.tileView(tileView: self, didDragToPoint: self.center)
}
}
2.TargetView.swift
import UIKit
class TargetView: UILabel {
var letter: Character!
var isMatch = false
init(letter: Character, sideLength: CGFloat) {
self.letter = letter
//let image = UIImage(named: "slot")
//super.init(image: image)
//let scale = CGRect(x: 0, y: 0, width: (image?.size.width)! * scale, height: (image?.size.height)! * scale)
super.init(frame: CGRect(x: 0, y: 0, width: 15, height: 30))
self.backgroundColor = .red
self.textAlignment = .center
self.font = .boldSystemFont(ofSize: 60.0 / 3)
self.text = "_"//String(letter).uppercased()
self.textColor = .white
self.lineBreakMode = .byWordWrapping
self.adjustsFontSizeToFitWidth = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
3. ViewController
import UIKit
class ViewController: UIViewController {
// let ScreenWidth = UIScreen.main.bounds.size.width
// let ScreenHeight = UIScreen.main.bounds.size.height
//frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height)
//var gameView = UIView()
private var tiles = [TileView]()
private var targets = [TargetView]()
let tileMargin : CGFloat = 0.0
var words = "Hellomyf rie nds?"
// lazy var stackView: UIStackView = {
// let stack = UIStackView()
// stack.axis = .horizontal
// stack.spacing = 0
// stack.distribution = .fillEqually
// stack.translatesAutoresizingMaskIntoConstraints = false
// return stack
// }()
//
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
dealRandomAnagram()
}
func dealRandomAnagram() {
let anagram1 = words
let anagram1length = anagram1.count
print("phrase1[\(anagram1length)]: \(anagram1)")
// Рассчет размера плитки //Tile size calculation
let tileSize = ceil(UIScreen.main.bounds.size.width * 0.8 / CGFloat(min(anagram1length, anagram1length))) + tileMargin // * 1
//Поиск х позиции первой плитки // Finding x position of the first tile
var xOffset = (UIScreen.main.bounds.size.width - CGFloat(max(anagram1length, anagram1length)) * (15 + tileMargin)) / 2.0
//+ tileMargin
//let yOffset = (UIScreen.main.bounds.size.height - CGFloat(max(anagram1length, anagram1length)) * (15 + tileMargin)) / 2.0
//+ tileMargin
//настройка центра плитки (вместо источника плитки)
xOffset += tileSize / 2.0
tiles = []
// создание плитки
for (index, letter) in anagram1.enumerated() {
if letter != " " {
let tile = TileView(letter: letter, sideLength: tileSize)
tile.center = CGPoint(x: xOffset + CGFloat(index) * (tileSize + tileMargin), y: UIScreen.main.bounds.size.height/4*3)
view.addSubview(tile)
tile.dragDelegate = self
tiles.append(tile)
}
}
targets = []
for (index,letter) in anagram1.enumerated() {
let target = TargetView(letter: letter, sideLength: 15)
if letter != " " || index > 15 {
// let target = TargetView(letter: letter, sideLength: 15)
target.center = CGPoint(x: xOffset + CGFloat(index) /* * 20 */ * (15 + tileMargin), y: UIScreen.main.bounds.size.height - 100) //100 //UIScreen.main.bounds.size.height - CGFloat(index) * 50
view.addSubview(target)
targets.append(target)
//stackView.addArrangedSubview(target)
//targets.append(target)
}
}
}
//xOffset + CGFloat(index) /* * 20 */ * (tileSize + tileMargin )
//UIScreen.main.bounds.size.height - 100
func placeTile(tileView: TileView, targetView: TargetView) {
tileView.isMatch = true
targetView.isMatch = true
tileView.isUserInteractionEnabled = false
UIView.animate(withDuration: 0.35, delay: 0.0, options: UIView.AnimationOptions.curveEaseOut, animations: {
tileView.center = targetView.center
tileView.backgroundColor = .clear
targetView.transform = .identity
}, completion: {
(value: Bool) in
targetView.isHidden = true
})
}
func checkValidate() {
for targetView in targets {
if !targetView.isMatch {
return
}
}
print("Valid")
}
}
extension ViewController: TileDragDelegateProtocol {
func tileView(tileView: TileView, didDragToPoint: CGPoint) {
var targetView: TargetView?
for tv in targets {
if tv.frame.contains(didDragToPoint) && !tv.isMatch {
targetView = tv
break
}
}
if let targetView = targetView {
//2 check if letter matches
if targetView.letter == tileView.letter {
//3
self.placeTile(tileView: tileView, targetView: targetView)
print("Success! You should place the tile here!")
//more stuff to do on success here
self.checkValidate()
print("Check if the player has completed the phrase")
} else {
//4
UIView.animate(withDuration:0.35,
delay:0.00,
options :UIView.AnimationOptions.curveEaseOut,
animations: {
tileView.center = CGPoint(x: tileView.center.x,
y: tileView.center.y - (200))
},
completion: nil)
print("Failure. Let the player know this tile doesn't belong here")
//more stuff to do on failure here
}
}
}
}

Related

how to change the size of a uiimageview appended to a empty array

My swift code uses a button to place imageviews on a uiviewcontroller that parent from a empty array. The problem is I dont know how to effect the size of the invidicual imageview after it is placed on the uiview controller. When a imageview is click and then when the slider is moved the imageview should change size. However what is happening is the value when the slider is changed effects the next iamgeview that is placed on the uiviewcontroller. Pitcure below is included I circled what the slider should effect. This is a video of what I am trying to do https://www.youtube.com/watch?v=ho6ID6XVEYw&feature=youtu.be.
import UIKit
class ViewController: UIViewController {
var sx = UISlider()
var count: Int = 0
var ht = -90
var ww = 80
var arrTextFields = [UIImageView]()
var b7 = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
[b7,sx].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
b7.frame = CGRect(x: view.center.x-115, y: view.center.y + 200, width: 70, height: 40)
sx.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 70, height: 40)
b7.addTarget(self, action: #selector(addBOx), for: .touchUpInside)
}
//func that adds imageview.
#objc func addBOx() {
let subview = UIImageView()
subview.isUserInteractionEnabled = true
arrTextFields.append(subview)
view.addSubview(subview)
sx.addTarget(self, action: #selector(ji), for: .valueChanged)
sx.minimumValue = 10
sx.maximumValue = 150
subview.frame = CGRect(x: view.bounds.midX - 0, y: view.bounds.midY + CGFloat(ht), width: CGFloat(ww), height: 35)
subview.backgroundColor = .purple
subview.tag = count
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestured(_:)))
subview.addGestureRecognizer(pan)
count += 1
ht += 50
arrTextFields.append(subview)
}
#objc func handlePanGestured(_ gesture: UIPanGestureRecognizer) {
let draggedView = gesture.view!
view.bringSubviewToFront(draggedView)
let translation = gesture.translation(in: view)
draggedView.center = CGPoint(x: draggedView.center.x + translation.x, y: draggedView.center.y + translation.y)
gesture.setTranslation(.zero, in: view)
}
#objc func ji(sender : UISlider){
ww = Int(sx.value)
}}
The main idea that needs to be implemented is to add the currentView variable to find out which view we want to change.
We also need to add some functionality to select the specific view that needs to be changed.
1) Add variable currentView
class ViewController: UIViewController {
...
var currentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
2) Add tapGestureRecognozer to func addBOx()
#objc func addBOx() {
...
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGestured(_:)))
subview.addGestureRecognizer(tapGesture)
}
3) Add handleTapGestured(_:)
#objc func handleTapGestured(_ gesture: UIPanGestureRecognizer) {
currentView = gesture.view
}
4) Update func ji(sender: UISlider)
#objc func ji(sender: UISlider) {
...
currentView?.bounds.size.width = CGFloat(sx.value)
}
All code:
import UIKit
class ViewController: UIViewController {
var sx = UISlider()
var count: Int = 0
var ht = -90
var ww = 80
var arrTextFields = [UIImageView]()
var b7 = UIButton()
var currentView: UIView?
override func viewDidLoad() {
super.viewDidLoad()
[b7,sx].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
b7.frame = CGRect(x: view.center.x-115, y: view.center.y + 200, width: 70, height: 40)
sx.frame = CGRect(x: view.center.x-115, y: view.center.y-200, width: 70, height: 40)
b7.addTarget(self, action: #selector(addBOx), for: .touchUpInside)
}
//func that adds imageview.
#objc func addBOx() {
let subview = UIImageView()
subview.isUserInteractionEnabled = true
arrTextFields.append(subview)
view.addSubview(subview)
sx.addTarget(self, action: #selector(ji), for: .valueChanged)
sx.minimumValue = 10
sx.maximumValue = 150
subview.frame = CGRect(x: view.bounds.midX - 0, y: view.bounds.midY + CGFloat(ht), width: CGFloat(ww), height: 35)
subview.backgroundColor = .purple
subview.tag = count
let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePanGestured(_:)))
subview.addGestureRecognizer(pan)
count += 1
ht += 50
arrTextFields.append(subview)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapGestured(_:)))
subview.addGestureRecognizer(tapGesture)
}
#objc func handleTapGestured(_ gesture: UIPanGestureRecognizer) {
currentView = gesture.view
}
#objc func handlePanGestured(_ gesture: UIPanGestureRecognizer) {
let draggedView = gesture.view!
view.bringSubviewToFront(draggedView)
let translation = gesture.translation(in: view)
draggedView.center = CGPoint(x: draggedView.center.x + translation.x, y: draggedView.center.y + translation.y)
gesture.setTranslation(.zero, in: view)
}
#objc func ji(sender : UISlider){
ww = Int(sx.value)
currentView?.bounds.size.width = CGFloat(sx.value)
}
}
Alternatively, you can add a textField and write the number that the element from your array needs to change.
And please, don't use variables like sx, ht, ww, b7 in your code.
You can read API Design Guidelines here

How to use SwiftUI with different views?

I have SCNCylinder in a SCNScene and a button in a SwiftUI frame below it. Whenever the button is pressed the cylinder is expected to rotate by 90°. I am not getting the expected result with my code.
//SwiftUI
struct ContentView: View {
var body: some View {
VStack{
Button(action: {
// What to perform
let rotationangle = 180.0
}) {
// How the button looks like
Text("90°")
.frame(width: 100, height: 100)
.position(x: 225, y: 500)
}
SceneKitView()
.frame(width: 300, height: 300)
.position(x: 0, y: 200)
}
}
}
//SceneKit
struct SceneKitView: UIViewRepresentable {
func makeUIView(context:
UIViewRepresentableContext<SceneKitView>) -> SCNView {
//Scene properties
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
//Cylinder properties
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
cylinder.firstMaterial?.diffuse.contents = UIColor.green
//Pivot and rotation of the cylinder
func degreesToRadians(_ degrees: Float) -> CGFloat {
return CGFloat(degrees * .pi / 180)
}
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
//Line with an error: Use of unresolved identifier ‘rotationangle’
let rotation = SCNAction.rotate(by: degreesToRadians(rotationangle), around: SCNVector3 (1, 0, 0), duration: 5)
sceneView.scene?.rootNode.addChildNode(cylindernode)
cylindernode.runAction(rotation)
return sceneView
}
func updateUIView(_ uiView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
}
typealias UIViewType = SCNView
}
I am having a error saying “ Use of unresolved identifier ‘rotationangle’ “ during using ‘rotationangle‘ in SCNAction.
You need to declare a #State in your ContentView and set the value of the property in your Button action.
You then need to declare an angle property in your SceneKitView as a #Binding and use that value for it to work properly.
I haven't yet tested this out. Keep in mind that you don't have cone declared anywhere so I'm not sure what that is.
import SwiftUI
import SceneKit
struct ContentView: View {
#State var rotationAngle: Float = 0.0
var body: some View {
VStack {
Button(action: {
// What to perform
self.rotationAngle = 180.0
}) {
// How the button looks like
Text("90°")
.frame(width: 100, height: 100)
.position(x: 225, y: 500)
}
SceneKitView(angle: self.$rotationAngle)
.frame(width: 300, height: 300)
.position(x: 0, y: 200)
}
}
}
//SceneKit
struct SceneKitView: UIViewRepresentable {
#Binding var angle: Float
func degreesToRadians(_ degrees: Float) -> CGFloat {
print(degrees)
return CGFloat(degrees * .pi / 180)
}
func makeUIView(context: UIViewRepresentableContext<SceneKitView>) -> SCNView {
// Scene properties
let sceneView = SCNView()
sceneView.scene = SCNScene()
sceneView.allowsCameraControl = true
sceneView.autoenablesDefaultLighting = true
sceneView.backgroundColor = UIColor.white
sceneView.frame = CGRect(x: 0, y: 10, width: 0, height: 1)
return sceneView
}
func updateUIView (_ sceneView: SCNView, context: UIViewRepresentableContext<SceneKitView>) {
if self.angle > 0 {
// Cylinder properties
let cylinder = SCNCylinder(radius: 0.02, height: 2.0)
let cylindernode = SCNNode(geometry: cylinder)
cylindernode.position = SCNVector3(x: 0, y: 0, z: 0)
// cone.firstMaterial?.diffuse.contents = UIColor.green
// Pivot and rotation of the cylinder
cylindernode.pivot = SCNMatrix4MakeTranslation(0, -1, 0)
sceneView.scene?.rootNode.addChildNode(cylindernode)
//Line with an error: Use of unresolved identifier ‘rotationangle’
let rotation = SCNAction.rotate(by: self.degreesToRadians(self.angle), around: SCNVector3 (1, 0, 0), duration: 5)
cylindernode.runAction(rotation)
}
}
}

How to insert data to a textfield that goes to an empty array and in turn displays in a label?

I found this ready code that prints out an already stored array called (let dataArr = ["25","51","57","73","68"]) in rows into a label.
I have now made a text field that can insert to another array(my array = String) I want this array to be swaped with the first array.
But know when I swap to empty array that has already been populated via a text field it doesn’t appear in label.
I was hoping to swap the dataArr to an empty array called array that files up with data when inserted via the text field but for reason when I am doing that it doesn’t get shown in the label I am posting now to see what I am doing wrong.
But when I print the array it appears in the console but not in the label.
let dataArr = ["2,5","5,1","5,7"]
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
var yPos = 90
for i in 0..<dataArr.count {
let element = dataArr[i]
let labelNum = UILabel()
let num1Nnum2 = "number 1 : \(element)"
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
self.view.addSubview(labelNum)
}
}
var array = [String]()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .yellow
var yPos = 90
for i in 0..<array.count {
let element = array[i]
let labelNum = UILabel()
let num1Nnum2 = "number 1 : \(element)"
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
self.view.addSubview(labelNum)
}
}
Ran your code and got the following result.
Is this what you were expecting?
UPDATE :
Here's what whipped up.
lazy var myText: UITextField = {
let input = UITextField(frame: CGRect(x: 0, y: 0, width: 300, height: 40))
input.backgroundColor = .white
input.placeholder = "Enter data here!"
return input
}()
lazy var enterButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = .gray
button.setTitle("ENTER", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 11)
button.addTarget(self, action: #selector(btnEnter), for: .touchUpInside)
return button
}()
lazy var doneButton: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
button.backgroundColor = .gray
button.setTitle("Done", for: .normal)
button.titleLabel?.font = UIFont.boldSystemFont(ofSize: 11)
button.addTarget(self, action: #selector(btnDone), for: .touchUpInside)
return button
}()
var array = [String]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if (!array.isEmpty) {
view.backgroundColor = .yellow
var yPos = 90
for i in 0..<array.count {
let element = array[i]
let labelNum = UILabel()
let num1Nnum2 = "number 1 : \(element)"
labelNum.text = num1Nnum2
labelNum.textAlignment = .center
labelNum.frame = CGRect( x:10, y:yPos, width:250, height: 80)
yPos += 80
myText.removeFromSuperview()
enterButton.removeFromSuperview()
doneButton.removeFromSuperview()
self.view.addSubview(labelNum)
}
}
else
{
view.backgroundColor = .green
setUpMyText()
setUpEnterButton()
setUpDoneButton()
}
}
private func setUpMyText() {
self.view.addSubview(myText)
myText.translatesAutoresizingMaskIntoConstraints = false
myText.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
myText.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true
myText.widthAnchor.constraint(equalToConstant: myText.bounds.width).isActive = true
myText.heightAnchor.constraint(equalToConstant: myText.bounds.height).isActive = true
}
private func setUpEnterButton() {
self.view.addSubview(enterButton)
enterButton.translatesAutoresizingMaskIntoConstraints = false
enterButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
enterButton.topAnchor.constraint(equalTo: myText.bottomAnchor, constant: 10).isActive = true
}
private func setUpDoneButton() {
self.view.addSubview(doneButton)
doneButton.translatesAutoresizingMaskIntoConstraints = false
doneButton.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
doneButton.topAnchor.constraint(equalTo: enterButton.bottomAnchor, constant: 10).isActive = true
}
#objc fileprivate func btnEnter() {
print(myText)
array.append(myText.text!)
myText.text = ""
}
#objc fileprivate func btnDone() {
print("Done")
self.viewDidLoad()
}

Accessing element in array, saving it and overwriting it

My Goal: to access an element in an SKnode array and its property between my two scenes and also save and retrieve it the array that contains my achievement SKNodes that hold different values.
So I have two scenes one that is my actual game scene and the second one being my achievements menu that contains the list of all my achievements. When the achievementMenu scene starts it produces 27 Achievements which are a subclass of SKNode called “Achievements” which is seen below”
It is made in a func like so:
for _ in 0...0 {
let Counter = 0
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
Achievement.zPosition = 100
Achievement.name = "Ach0"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
Here is the Subclass of the SKNode that holds all the achievements Properties:
class Achievements: SKNode {
//Nodes used throughout the SKNode class
var achievementLabel = SKLabelNode()
var achievementTitleLabel = SKLabelNode()
var achievementNode = SKSpriteNode()
//Counter used to determine the Achievements properties
var achCounter = 0
//Amount Variables used as Achievement Properties
var image: String = "locked"
var information: String = ""
var title: String = ""
var amount = 0
var neededAmount = 0
var incrementAmount = 0
var incrementAmount2 = 0
var stage = 0
func createAchievement() {
let tex:SKTexture = SKTexture(imageNamed: image)
achievementNode = SKSpriteNode(texture: tex, color: SKColor.black, size: CGSize(width: 75, height: 75)) //frame.maxX / 20, height: frame.maxY / 20))
achievementNode.zPosition = -10
achievementNode.name = "AchievementNode"
self.addChild(achievementNode)
self.zPosition = -11
createAchievementLabels()
}
func createAchievementLabels() {
achievementTitleLabel = SKLabelNode(fontNamed: "Avenir-Black")
achievementTitleLabel.fontColor = UIColor.black;
achievementTitleLabel.fontSize = 13 //self.frame.maxY/30
achievementTitleLabel.position = CGPoint (x: 0, y: 45)
achievementTitleLabel.text = "\(title)"
achievementTitleLabel.zPosition = -9
addChild(achievementTitleLabel)
achievementTitleLabel.name = "AchievementTitleLabel"
achievementLabel = SKLabelNode(fontNamed: "Avenir-Black")
achievementLabel.fontColor = UIColor.black;
achievementLabel.fontSize = 13 //self.frame.maxY/30
achievementLabel.position = CGPoint (x: 0, y: -50)
achievementLabel.text = ("\(amount) / \(neededAmount)")
achievementLabel.zPosition = -9
addChild(achievementLabel)
achievementLabel.name = "AmountLabel"
}
func UpdateText() {
achievementLabel.text = ("\(amount) / \(neededAmount)")
}
func getAchievementProperties(){
switch achCounter {
case 0:
amount = 0;
neededAmount = 10;
incrementAmount = 100;
incrementAmount2 = 1000;
information = "No. Deaths"
title = "DAILY"
stage = 0
case 1:
amount = 0;
neededAmount = 5;
incrementAmount = 10;
incrementAmount2 = 70;
information = "No. Deaths"
title = "DAILY"
stage = 0
these are then put into a global array variable that is called “Achievements3” that is defined like so:
var Achievements3 = [SKNode]()
I am trying to get the achievements array to save when the game is “switched off” and when it is switched back on for it to replace the new nodes it generates in the AchievementMenu scene with the existing ones so that the data can be written over and the progress is still there. Along with this i am also trying to add one to the amount property of a certain achievement that is contained within the Array when something happens in the game scene such as the player dying for example. I can’t seem to find a way to access a specific element in an array and then add to its property it was given when it was generated from the sub class. I was going to use the NSUserdefaults to store all the SKNodes individually but It is too much just to store each individual achievement.
Can some please tell me what i’m doing wrong. if you would like to see my whole achievement menu scene it can be viewed below:
import Foundation
import SpriteKit
var Achievements3 = [SKNode]()
class AchievementMenu: SKScene {
//Array for Achievement Sprites
//establishing scroll
var startY: CGFloat = 0.0
var lastY: CGFloat = 0.0
var moveableArea = SKNode()
//Universal Buttons
var characterMenu = SKSpriteNode()
var itemShopMenu = SKSpriteNode()
var achievementMenu = SKSpriteNode()
var universalBackButton = SKSpriteNode()
//Labels for Sections of Achievements
var dailySection = SKLabelNode()
var achievementSection = SKLabelNode()
var bossAchievementSection = SKLabelNode()
var exitInfoMenu = SKSpriteNode()
var TEMPBUTTONTOADDSTUFF = SKSpriteNode()
override func didMove(to view: SKView) {
//setting up scrollling
moveableArea.position = CGPoint(x: 0, y: 0)
moveableArea.zPosition = -12
self.addChild(moveableArea)
//Labels for Dividing Sections of the Achievement Menu
dailySection = SKLabelNode(fontNamed: "Avenir-Black"); dailySection.fontColor = UIColor.black; dailySection.fontSize = self.frame.maxY/30
dailySection.position = CGPoint(x:self.frame.midX, y:self.self.frame.midY * 1.27)
dailySection.text = "--------------------------Daily-------------------------"
moveableArea.addChild(dailySection)
achievementSection = SKLabelNode(fontNamed: "Avenir-Black"); achievementSection.fontColor = UIColor.black; achievementSection.fontSize = self.frame.maxY/30
achievementSection.position = CGPoint(x:self.frame.midX, y:self.self.frame.midY * 0.65)
achievementSection.text = "--------------------------Achievements-------------------------"
moveableArea.addChild(achievementSection)
bossAchievementSection = SKLabelNode(fontNamed: "Avenir-Black"); bossAchievementSection.fontColor = UIColor.black; bossAchievementSection.fontSize = self.frame.maxY/30
bossAchievementSection.position = CGPoint(x:self.frame.midX, y:self.self.frame.midY * -1.3)
bossAchievementSection.text = "--------------------------Boss-------------------------"
moveableArea.addChild(bossAchievementSection)
//Buttons For Navigating the Interface
universalBackButton = SKSpriteNode(color: SKColor.orange, size: CGSize(width: 75, height: 50))
universalBackButton.position = CGPoint(x:50, y: 35)
universalBackButton.zPosition = 100001
addChild(universalBackButton)
characterMenu = SKSpriteNode(color: SKColor.orange, size: CGSize(width: 75, height: 50))
characterMenu.position = CGPoint(x:142.5, y: 35)
characterMenu.zPosition = 100001
addChild(characterMenu)
itemShopMenu = SKSpriteNode(color: SKColor.orange, size: CGSize(width: 75, height: 50))
itemShopMenu.position = CGPoint(x:232.5, y: 35)
itemShopMenu.zPosition = 100001
addChild(itemShopMenu)
achievementMenu = SKSpriteNode(color: SKColor.red, size: CGSize(width: 75, height: 50))
achievementMenu.position = CGPoint(x:325, y: 35)
achievementMenu.zPosition = 100001
addChild(achievementMenu)
exitInfoMenu = SKSpriteNode(color: SKColor.darkGray, size: CGSize(width: 75, height: 50))
exitInfoMenu.position = CGPoint(x:frame.midX, y: frame.midY - frame.midY / 2)
exitInfoMenu.zPosition = 100001
exitInfoMenu.isHidden = true
addChild(exitInfoMenu)
TEMPBUTTONTOADDSTUFF = SKSpriteNode(color: SKColor.orange, size: CGSize(width: 75, height: 50))
TEMPBUTTONTOADDSTUFF.position = CGPoint(x:frame.midX, y: 550)
TEMPBUTTONTOADDSTUFF.zPosition = 100
addChild(TEMPBUTTONTOADDSTUFF)
createAchievements()
}
func createAchievements() {
for _ in 0...0 {
//let Achievement = SKSpriteNode(color: SKColor.blue, size: CGSize(width: 100, height: 100))
let Counter = 0
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
Achievement.zPosition = 100
Achievement.name = "Ach0"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
for _ in 0...0 {
let Counter = 1
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX*0.35, y: self.frame.midY)
Achievement.zPosition = 100
moveableArea.addChild(Achievement)
Achievement.name = "Ach1"
Achievements3.append(Achievement)
}
for _ in 0...0 {
let Counter = 2
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX*1.65, y: self.frame.midY)
Achievement.zPosition = 100
Achievement.name = "Ach2"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
for _ in 0...0 {
let Counter = 3
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX, y: self.frame.midY*0.4)
Achievement.zPosition = 100
Achievement.name = "Ach3"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
for _ in 0...0 {
let Counter = 4
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX*0.35, y: self.frame.midY*0.4)
Achievement.zPosition = 100
Achievement.name = "Ach4"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
for _ in 0...0 {
let Counter = 5
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX*1.65, y: self.frame.midY*0.4)
Achievement.zPosition = 100
Achievement.name = "Ach5"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
for _ in 0...0 {
let Counter = 6
let Achievement = Achievements()
Achievement.achCounter = Counter
Achievement.getAchievementProperties()
Achievement.position = CGPoint(x: self.frame.midX, y: self.frame.midY*0.05)
Achievement.zPosition = 100
Achievement.name = "Ach6"
Achievements3.append(Achievement)
moveableArea.addChild(Achievement)
}
}
func BringUpAchievementInfo(achName: String) {
var title = SKLabelNode()
var info = SKLabelNode()
var atexture = SKSpriteNode()
var amountLabel = SKLabelNode()
var stageLabel = SKLabelNode()
let node45 = SKNode()
node45.zPosition = 997
node45.name = "LOL"
addChild(node45)
exitInfoMenu.isHidden = false
universalBackButton.isHidden = true; characterMenu.isHidden = true; itemShopMenu.isHidden = true; achievementMenu.isHidden = true
let backgroundNode = SKSpriteNode(color: SKColor.darkGray, size: CGSize(width: frame.maxX, height: frame.maxY))
backgroundNode.position = CGPoint(x: frame.midX, y: frame.midY)
backgroundNode.alpha = 0.5
backgroundNode.zPosition = 998
node45.addChild(backgroundNode)
let infoNode = SKSpriteNode(color: SKColor.white, size: CGSize(width: frame.size.width, height: frame.size.height / 2))
infoNode.position = CGPoint(x: frame.midX, y: frame.midY + frame.midY / 6)
infoNode.zPosition = 999
node45.addChild(infoNode)
moveableArea.enumerateChildNodes(withName: achName ) {
node, stop in
let achievements = node as! Achievements
if node is Achievements {
title = SKLabelNode(fontNamed: "Avenir-Black"); title.fontColor = UIColor.black; title.fontSize = 25
title.position = CGPoint(x: 0, y: 100)
title.text = "\(achievements.title)"
title.zPosition = 1001
infoNode.addChild(title)
info = SKLabelNode(fontNamed: "Avenir-Black"); info.fontColor = UIColor.black; info.fontSize = 15
info.position = CGPoint(x: 0, y: -80)
info.text = "\(achievements.information)"
info.zPosition = 1001
infoNode.addChild(info)
amountLabel = SKLabelNode(fontNamed: "Avenir-Black"); amountLabel.fontColor = UIColor.black; amountLabel.fontSize = 15
amountLabel.position = CGPoint(x: 0, y: -100)
amountLabel.text = ("\(achievements.amount) / \(achievements.neededAmount)")
amountLabel.zPosition = 1001
infoNode.addChild(amountLabel)
atexture = SKSpriteNode(texture: achievements.achievementNode.texture, color: SKColor.blue, size: CGSize(width: 100, height: 100 ))
atexture.position = CGPoint(x: 0, y: 20)
atexture.zPosition = 1000
infoNode.addChild(atexture)
stageLabel = SKLabelNode(fontNamed: "Avenir-Black"); stageLabel.fontColor = UIColor.black; stageLabel.fontSize = 15
stageLabel.position = CGPoint(x: 0, y: -140)
stageLabel.text = ("Stage: \(achievements.stage)")
stageLabel.zPosition = 1001
infoNode.addChild(stageLabel)
}
}
}
func addOneToAchievementAndUpdateText(name1: String) {
moveableArea.enumerateChildNodes(withName: name1) {
node, stop in
if node is Achievements {
let achievements = node as! Achievements
achievements.amount += 1
achievements.UpdateText()
self.checkForAchievementStageCompletion(name2: name1)
}
}
}
func checkForAchievementStageCompletion(name2: String) {
moveableArea.enumerateChildNodes(withName: name2) {
node, stop in
if node is Achievements {
let achievements = node as! Achievements
if achievements.amount == achievements.neededAmount {
achievements.enumerateChildNodes(withName: "AchievementNode") {
node, stop in
if achievements.stage == 0 {
let changeTexture = SKAction.setTexture(SKTexture(imageNamed: "diamonds1"))
node.run(changeTexture)
achievements.neededAmount = achievements.incrementAmount
achievements.stage = 1
} else if achievements.stage == 1 {
let changeTexture = SKAction.setTexture(SKTexture(imageNamed: "diamonds2"))
node.run(changeTexture)
achievements.neededAmount = achievements.incrementAmount2
achievements.stage = 2
} else if achievements.stage == 2 {
let changeTexture = SKAction.setTexture(SKTexture(imageNamed: "diamondicon"))
node.run(changeTexture)
achievements.stage = 3
}
}
}
}
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let location = touch.location(in: self)
let node = self.atPoint(touch.location(in: self))
startY = location.y
lastY = location.y
switch node {
case universalBackButton:
let scene = GameScene(size: self.view!.bounds.size)
GameState.current = .gameStart
scene.scaleMode = .aspectFill
scene.backgroundColor = UIColor.clear
let transition = SKTransition.crossFade(withDuration: 0.5)
self.scene?.view?.presentScene(scene, transition: transition)
case characterMenu:
let scene = CharacterMenu(size: self.view!.bounds.size)
scene.scaleMode = .aspectFill
scene.backgroundColor = UIColor.white
self.scene?.view?.presentScene(scene)
case itemShopMenu:
let scene = ShopItemMenu(size: self.view!.bounds.size)
scene.scaleMode = .aspectFill
scene.backgroundColor = UIColor.white
self.scene?.view?.presentScene(scene)
case exitInfoMenu:
let node = childNode(withName: "LOL")
node?.removeFromParent()
exitInfoMenu.isHidden = true
universalBackButton.isHidden = false; characterMenu.isHidden = false; itemShopMenu.isHidden = false; achievementMenu.isHidden = false
case TEMPBUTTONTOADDSTUFF:
addOneToAchievementAndUpdateText(name1: "Ach0")
addOneToAchievementAndUpdateText(name1: "Ach1")
addOneToAchievementAndUpdateText(name1: "Ach2")
case Achievements3[0]:
BringUpAchievementInfo(achName: "Ach0")
case Achievements3[1]:
BringUpAchievementInfo(achName: "Ach1")
case Achievements3[2]:
BringUpAchievementInfo(achName: "Ach2")
case Achievements3[3]:
BringUpAchievementInfo(achName: "Ach3")
case Achievements3[4]:
BringUpAchievementInfo(achName: "Ach4")
case Achievements3[5]:
BringUpAchievementInfo(achName: "Ach5")
case Achievements3[6]:
BringUpAchievementInfo(achName: "Ach6")
default: return
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first!
let location = touch.location(in: self)
let currentY = location.y
// Set Top and Bottom scroll distances, measured in screenlengths
let topLimit:CGFloat = 0.0
let bottomLimit:CGFloat = 1.33
// Set scrolling speed - Higher number is faster speed
let scrollSpeed:CGFloat = 0.2
// calculate distance moved since last touch registered and add it to current position
let newY = moveableArea.position.y + ((currentY - lastY)*scrollSpeed)
// perform checks to see if new position will be over the limits, otherwise set as new position
if newY < self.size.height*(-topLimit) {
moveableArea.position = CGPoint(x: moveableArea.position.x, y: self.size.height*(-topLimit))
}
else if newY > self.size.height*bottomLimit {
moveableArea.position = CGPoint(x: moveableArea.position.x, y: self.size.height*bottomLimit)
}
else {
moveableArea.position = CGPoint(x: moveableArea.position.x, y: newY)
}
}
}
class Achievements: SKNode {
//Nodes used throughout the SKNode class
var achievementLabel = SKLabelNode()
var achievementTitleLabel = SKLabelNode()
var achievementNode = SKSpriteNode()
//Counter used to determine the Achievements properties
var achCounter = 0
//Amount Variables used as Achievement Properties
var image: String = "locked"
var information: String = ""
var title: String = ""
var amount = 0
var neededAmount = 0
var incrementAmount = 0
var incrementAmount2 = 0
var stage = 0
func createAchievement() {
let tex:SKTexture = SKTexture(imageNamed: image)
achievementNode = SKSpriteNode(texture: tex, color: SKColor.black, size: CGSize(width: 75, height: 75)) //frame.maxX / 20, height: frame.maxY / 20))
achievementNode.zPosition = -10
achievementNode.name = "AchievementNode"
self.addChild(achievementNode)
self.zPosition = -11
createAchievementLabels()
}
func createAchievementLabels() {
achievementTitleLabel = SKLabelNode(fontNamed: "Avenir-Black")
achievementTitleLabel.fontColor = UIColor.black;
achievementTitleLabel.fontSize = 13 //self.frame.maxY/30
achievementTitleLabel.position = CGPoint (x: 0, y: 45)
achievementTitleLabel.text = "\(title)"
achievementTitleLabel.zPosition = -9
addChild(achievementTitleLabel)
achievementTitleLabel.name = "AchievementTitleLabel"
achievementLabel = SKLabelNode(fontNamed: "Avenir-Black")
achievementLabel.fontColor = UIColor.black;
achievementLabel.fontSize = 13 //self.frame.maxY/30
achievementLabel.position = CGPoint (x: 0, y: -50)
achievementLabel.text = ("\(amount) / \(neededAmount)")
achievementLabel.zPosition = -9
addChild(achievementLabel)
achievementLabel.name = "AmountLabel"
}
func UpdateText() {
achievementLabel.text = ("\(amount) / \(neededAmount)")
}
func getAchievementProperties(){
switch achCounter {
case 0:
amount = 0;
neededAmount = 10;
incrementAmount = 100;
incrementAmount2 = 1000;
information = "No. Deaths"
title = "DAILY"
stage = 0
case 1:
amount = 0;
neededAmount = 5;
incrementAmount = 10;
incrementAmount2 = 70;
information = "No. Deaths"
title = "DAILY"
stage = 0
case 2:
amount = 0;
neededAmount = 70;
incrementAmount = 10;
incrementAmount2 = 70;
information = "No. Deaths"
title = "DAILY"
stage = 0
case 3:
amount = 0;
neededAmount = 70;
incrementAmount = 10;
incrementAmount2 = 70;
information = "No. Deaths"
title = "ROOKIE"
stage = 0
case 4:
amount = 0;
neededAmount = 70;
incrementAmount = 10;
incrementAmount2 = 70;
information = "No. Deaths"
title = "PLAYER"
stage = 0
default: return
}
createAchievement()
}
}

combine an array of images to a single image which is a row of images in swift

Trying to combine an array of images to a single image which looks like a long row of the images.
(note: images may or may not be of similar sizes)
here is the code, which currently works for single size images only
func combine(images: NSMutableArray) -> UIImage {
var contextSize = CGSizeZero
var xMaxSize : CGFloat = 0
for image in images {
//
xMaxSize = xMaxSize + image.size.width
//contextSize.width = max(contextSize.width , image.size.width )
contextSize.height = max(contextSize.height, image.size.height)
}
contextSize.width = xMaxSize
UIGraphicsBeginImageContextWithOptions(contextSize, false, UIScreen.mainScreen().scale)
var imageXPlace :CGFloat = 0
for image in images {
let originX = imageXPlace
let originY = (contextSize.height - image.size.height) / 2
image.drawInRect(CGRectMake(originX, originY, image.size.width, image.size.height))
imageXPlace = imageXPlace + image.size.width
}
let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return combinedImage
}
This question is also related to this one
Images has different sizes
Height of the combined image - the smallest height in UIImage
ViewController.swift
import UIKit
struct ImageToCombine {
var image = UIImage()
var size: CGSize {
get {
return CGSize(width: image.size.width, height: image.size.height)
}
}
var widthAndHeightRatio: CGFloat {
get {
return image.size.width/image.size.height
}
}
init (image: UIImage) {
self.image = image
}
}
class ViewController: UIViewController {
var imageView = UIImageView()
var images = [ImageToCombine]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
imageView = UIImageView(frame: UIScreen.mainScreen().bounds)
imageView.contentMode = .ScaleAspectFit
view.addSubview(imageView)
images.append(ImageToCombine(image: UIImage(named: "image1")!))
images.append(ImageToCombine(image: UIImage(named: "image2")!))
images.append(ImageToCombine(image: UIImage(named: "image3")!))
imageView.image = combine(images, spaceBetweenImages: 10)
}
func combine(images: [ImageToCombine], spaceBetweenImages:CGFloat) -> UIImage {
var fullWidth:CGFloat = 0
var minHeight:CGFloat = images[0].size.height
for index in 0..<images.count {
fullWidth += images[index].size.width
if (index+1 < images.count) {
fullWidth += spaceBetweenImages
}
if (minHeight > images[index].size.height) {
minHeight = images[index].size.height
}
}
let quality = CGFloat(4)
let boundForBetter = CGRectMake(0, 0, fullWidth*quality, minHeight*quality)
UIGraphicsBeginImageContext(boundForBetter.size)
let context = UIGraphicsGetCurrentContext()
for index in 0..<images.count {
var imageX:CGFloat = 0
for index2 in 0..<index {
imageX += images[index2].size.width
if (index2+1 < images.count) {
imageX += spaceBetweenImages
}
}
let imageY:CGFloat = (minHeight - images[index].size.height)*quality/2
let rect = CGRect(x: imageX*quality, y: imageY, width: images[index].size.width*quality, height: images[index].size.height*quality)
CGContextDrawImage(context, rect, images[index].image.CGImage)
}
let combinedImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
let resultImageimage = UIImage(CGImage: imageResize(combinedImage, sizeChange:CGSize(width: fullWidth/quality, height: minHeight/quality)).CGImage!, scale: 1.0, orientation: .DownMirrored)
return resultImageimage
}
func imageResize(imageObj:UIImage, sizeChange:CGSize)-> UIImage {
let hasAlpha = true
let scale: CGFloat = 0.0 // Automatically use scale factor of main screen
UIGraphicsBeginImageContextWithOptions(sizeChange, !hasAlpha, scale)
imageObj.drawInRect(CGRect(origin: CGPointZero, size: sizeChange))
let scaledImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext() // !!!
return scaledImage
}
}
result:

Resources