How to wait for user to click UILabel? - ios

I want to make a while loop wait until a user clicks on a UILabel in a Swift Playground on Xcode. How can I do this?
Here's my loop
func gameLoop() {
while(score >= 0) {
let n = arc4random_uniform(3)
if(n == 0) {
opt1.text = rightStatements.randomElement()
opt2.text = wrongStatements.randomElement()
opt3.text = wrongStatements.randomElement()
} else if(n == 1) {
opt1.text = wrongStatements.randomElement()
opt2.text = rightStatements.randomElement()
opt3.text = wrongStatements.randomElement()
} else if(n == 2) {
opt1.text = wrongStatements.randomElement()
opt2.text = wrongStatements.randomElement()
opt3.text = rightStatements.randomElement()
}
}
}
For example, I want to wait until the user either clicks opt1, opt2, or opt3 Then do something based on what the user clicks.

Use Buttons instead of Labels and assign tag = 1, 2, and 3 for buttons. Create an IBAction function for the buttons and connect all the buttons to the same function.
Make variable 'n' as global.
var n = Int()
func nextAttempt() {
if(score >= 0) {
n = arc4random_uniform(3)
if(n == 0) {
opt1.text = rightStatements.randomElement()
opt2.text = wrongStatements.randomElement()
opt3.text = wrongStatements.randomElement()
} else if(n == 1) {
opt1.text = wrongStatements.randomElement()
opt2.text = rightStatements.randomElement()
opt3.text = wrongStatements.randomElement()
} else if(n == 2) {
opt1.text = wrongStatements.randomElement()
opt2.text = wrongStatements.randomElement()
opt3.text = rightStatements.randomElement()
}
}
else
{
//Score < 0
//Game Over
}
}
#IBAction func onButtonClick(_ sender: Any)
{
switch(sender.tag)
{
case 1:
if (n==0)
{
//Right button tapped
//Update score if you want
}
else
{
self.nextAttempt()
}
case 2:
if (n==1)
{
//Right button tapped
//Update score if you want
}
else
{
self.nextAttempt()
}
case 3:
if (n==2)
{
//Right button tapped
//Update score if you want
}
else
{
self.nextAttempt()
}
}
}
Hope this helps you!!

Related

SceneKit. Place nodes in one "surface"

The users draw a line on drawing views and then I need to translate these points into 3d world, but place these points in one "surface". For this, I map the array of points into vectors (I use hitTest with .featurePoint) and then filter this array for the further one
func didEndDrawing(with points: [CGPoint]) {
guard let transform = sceneView.pointOfView?.transform else { return }
let cameraVectror = SCNVector3(transform.m31, transform.m32, transform.m33)
let farthestVector = points
.reduce((vector: SCNVector3(0, 0, 0), distance: CGFloat.zero)) { result, point in
guard let vector = getVector(for: point) else { return result }
let distance: CGFloat = cameraVectror.distance(to: vector)
return distance > result.distance ? (vector, distance) : result
}
.vector
}
let parentNode = SCNNode()
parentNode.position = farthestVector
How can I adjust coordinates (I guess z position) to have all the child nodes at the same distance from the point of view?
The idea of the app is freehand drawing in AR.
Update
With Voltan's help I was able to solve it
points.forEach { point in
let scenePoint = sceneView.unprojectPoint(SCNVector3(point.x, point.y, CGFloat(projectedPoint.z)))
let sphere = SCNSphere(radius: 0.01)
let material = SCNMaterial()
material.diffuse.contents = UIColor.green
sphere.materials = [material]
let node = SCNNode(geometry: sphere)
node.position = scenePoint
sceneView.scene.rootNode.addChildNode(node)
}
If I'm understanding correctly, you want some kind of tap/drag combination - get the points from the 2D world and translate to a 3D world. This is some game code for a missile command type game, maybe it will help you with unprojectPoint stuff. There are some timers that aren't included, but hopefully you will get the idea.
#objc func handleTap(recognizer: UITapGestureRecognizer)
{
if(data.gameState == .endGame)
{
endGameAnimates.stop()
let _ = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in self.dismiss(animated: false, completion: nil) })
return
}
if(gameControl.isWaveComplete == true || gNodes.gameNodes.isPaused == true) { return }
currentLocation = recognizer.location(in: gameScene)
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(currentLocation.x, currentLocation.y, CGFloat(projectedPoint.z)))
if(data.gameState == .endGame) // Allow animations to finish, otherwise they will show up next round
{
DispatchQueue.main.async { self.endGameAnimates.stop() }
let _ = Timer.scheduledTimer(withTimeInterval: 1, repeats: false, block: { _ in self.dismiss(animated: false, completion: nil) })
return
}
if(data.missilesAvailable <= 0)
{
sound.playSoundType(vSoundType: .defenseFails)
hudControl.notify()
}
else
{
gameControl.defenseMissileShoot(vPosition: scenePoint, soundType: 0)
sound.playSoundType(vSoundType: .defenseFires)
}
}
//**************************************************************************
#objc func handlePan(recognizer: UIPanGestureRecognizer)
{
currentLocation = recognizer.location(in: gameScene)
let projectedPoint = gameScene.projectPoint(SCNVector3(0, 0, 0))
let scenePoint = gameScene.unprojectPoint(SCNVector3(currentLocation.x, currentLocation.y, CGFloat(projectedPoint.z)))
if(gameControl.isWaveComplete == true || gNodes.gameNodes.isPaused == true) { return }
switch recognizer.state
{
case UIGestureRecognizer.State.began:
gameControl.defenseMissileShoot(vPosition: scenePoint, soundType: 1)
SNDdefenseSoundCount = 0
if(data.missilesAvailable <= 0) { sound.playSoundType(vSoundType: .defenseFails); hudControl.notify() }
beginLocation.x = currentLocation.x
break
case UIGestureRecognizer.State.changed:
if(currentLocation.x > beginLocation.x + dragDistance)
{
beginLocation.x = currentLocation.x
if(data.missilesAvailable > 0) { gameControl.defenseMissileShoot(vPosition: scenePoint, soundType: 2) }
SNDdefenseSoundCount += 1
}
if(currentLocation.x < beginLocation.x - dragDistance)
{
beginLocation.x = currentLocation.x
if(data.missilesAvailable > 0) { gameControl.defenseMissileShoot(vPosition: scenePoint, soundType: 2) }
SNDdefenseSoundCount += 1
}
break
case UIGestureRecognizer.State.ended:
if(data.missilesAvailable > 0)
{
if(SNDdefenseSoundCount < 2) { sound.playSoundType(vSoundType: .defenseFires) }
else { sound.playSoundType(vSoundType: .defensePans) }
}
break
default:
break
}

How to make a number not repeat itself more than 2 times?

everyone ! Lets say we have
let random = arc4random_uniform(6)
how do i make it not repeat the same number more then two times ? I tried doing it like this :
let previousNumber = Int()
let lastNumber = Int ()
let random = Int(arc4random_uniform(6))
if random == previousNumber {
lastNumber = previousNumber
} else {
previousNumber = random
}
if random == lastNumber {
random = Int(arc4random_uniform(6))
}
But it didn't work. I am new to swift and i didn't find a topic about this on the new swift 3 code. Thank you !
First of all lets build a class to save the recent history of the selected values
class History {
private let size: Int
private var values = [Int]()
init(size:Int) {
self.size = size
}
func add(value: Int) {
values.insert(value, at: 0)
if values.count > size {
values.removeLast()
}
}
var repeatedValueOnFullHistory: Int? {
guard Set(values).count <= 1 else { return nil }
return values.first
}
}
Next let build a Randomizer
class Randomizer {
private var allValues = [Int]()
private var history: History
init?(maxValue: Int) {
guard maxValue > 0 else { return nil }
self.allValues = Array(0...maxValue)
self.history = History(size: maxValue + 1)
}
var next: Int {
let excludedValue = history.repeatedValueOnFullHistory
let allowedValues = allValues.filter { excludedValue != $0 }
let randomIndex = Int(arc4random_uniform(UInt32(allowedValues.count)))
let nextValue = allowedValues[randomIndex]
history.add(value: nextValue)
return nextValue
}
}
And finally let test it
if let r = Randomizer(maxValue: 6) {
r.next // 6
r.next // 2
r.next // 1
r.next // 4
r.next // 6
r.next // 4
r.next // 1
}

Make PanGesture becoming to slider

I made a PanGuesture as a slider, Here blow is the responds func:
func respondsToPenGesture(sender: UIPanGestureRecognizer) {
if (sender.state == UIGestureRecognizerState.Began) {
if noFilterEffectButton.selected == false {
startPanLocation = sender.locationInView(self.newEffectView)
persentageNumber.text = String(startNumber)
persentageNumber.hidden = false
}
} else if (sender.state == UIGestureRecognizerState.Changed) {
let stopLocation = sender.locationInView(self.newEffectView)
let abscissaChange = stopLocation.x - startPanLocation!.x
if newEffectView.hidden == false {
if abs(abscissaChange) > 0 {
if noFilterEffectButton.selected == false{
if let effectCurrent = self.currentEffect {
effectCurrent.adjustParams(CGFloat(abscissaChange/6))
}
}
}
if noFilterEffectButton.selected == false {
var printChangingNumber = startNumber + Int(abscissaChange)
if printChangingNumber > 100 {
printChangingNumber = 100
} else if printChangingNumber < 0 {
printChangingNumber = 0
}
persentageNumber.text = String(printChangingNumber)
}
}
} else if (sender.state == UIGestureRecognizerState.Ended) {
let stopLocation = sender.locationInView(self.newEffectView)
let abscissaChange = stopLocation.x - startPanLocation!.x
startNumber = startNumber + Int(abscissaChange)
if startNumber > 100 {
startNumber = 100
} else if startNumber < 0 {
startNumber = 0
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5*Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
self.persentageNumber.text = String(self.startNumber)
self.persentageNumber.hidden = true
}
}
}
percentageNumber is a label show the changes on the screen. And the adjustParams() function:
func adjustParams(value: CGFloat) {
newValue = value + newValue
self.adjust = newValue
print("AdjustNumber")
print(adjust)
}
And I override this function for exactly one effect:
override func adjustParams(value: CGFloat) {
super.adjustParams(value)
lastAutValueChange = lastAutValueChange + value/200
lastIntensityChange = lastIntensityChange + value/200
var currentAutValue = lastAutValueChange
var currentIntValue = lastIntensityChange
currentIntValue = currentIntValue < -1.0 ? -1.0 : currentIntValue
//value = value > 1.0 ? 1.0 : value
//autValue = value < -1.0 ? -1.0 : autValue
if (currentIntValue >= 0) {
currentIntValue = 1.0
} else {
currentIntValue += 1
}
if (currentAutValue <= 0) {
currentAutValue = 0
} else {
if currentAutValue > 1 {
currentAutValue = 1
}
}
print(currentIntValue)
print(currentAutValue)
self.autoTuneModule.setIntensity(Float(currentIntValue))
self.audioUnit.finalMix = Double(currentAutValue)
}
The test answer is not matchable for the exactly changes and the percentNumber shows on the screen. How could I change my functions to make them matchable for each others?
(By the way, I want to do something like the slider in Prisma, if there is something like it, please let me know about it).

Maths formula of cleaner way for bounding box logic?

Below I have written some bounding box logic which is a bit verbose. Is there a way to make it more readable, so the check looks cleaner and more concise?
func validatePosition(position:SCNVector3, forNode node:SCNNode) -> SCNVector3 {
var newPosition = position
var maxVector = SCNVector3Zero
var minVector = SCNVector3Zero
let success = self.actionDelegate?.getBoundingBox(&minVector, max: &maxVector)
guard success == true else {
return newPosition
}
if newPosition.x < minVector.x && newPosition.x < 0 {
newPosition.x = minVector.x
}
if newPosition.y < minVector.y && newPosition.y < 0 {
newPosition.y = minVector.y
}
if newPosition.z < minVector.z && newPosition.z < 0 {
newPosition.z = minVector.z
}
if newPosition.x > maxVector.x && newPosition.x > 0 {
newPosition.x = maxVector.x
}
if newPosition.y > maxVector.y && newPosition.y > 0 {
newPosition.y = maxVector.y
}
if newPosition.z > maxVector.z && newPosition.z > 0 {
newPosition.z = maxVector.z
}
return newPosition
}
Try this:
extension ClosedInterval {
func clamp(value : Bound) -> Bound {
return self.start > value ? self.start
: self.end < value ? self.end
: value
}
}
extension SCNVector3 {
func clamp(min min:SCNVector3, max: SCNVector3) -> SCNVector3 {
let x = (min.x...max.x).clamp(self.x)
let y = (min.y...max.y).clamp(self.y)
let z = (min.z...max.z).clamp(self.z)
return SCNVector3(x, y, z)
}
}
func validatePosition(position:SCNVector3, forNode node:SCNNode) -> SCNVector3 {
var newPosition = position
var maxVector = SCNVector3Zero
var minVector = SCNVector3Zero
let success = self.actionDelegate?.getBoundingBox(&minVector, max: &maxVector)
guard success == true else {
return newPosition
}
newPosition = position.clamp(min: minVector, max: maxVector)
return newPosition
}

Stuck in a loop. Very strange because the code sometimes work and sometimes just freezes

I am writing a puzzle game for an IOS. In my code I need to fill an array with some random (and non-random numbers) that will represent the main data structure.
func Random(r : Range<Int>) -> Int {
return Int(arc4random_uniform(UInt32(r.endIndex - r.startIndex))) + r.startIndex
} // function to generate random number
var arrWithColors = [Int]() // array that will hold the numbers
//function to that check if an array contains a number in a range already
func checkForNumberInArrayWithRange(r: Range <Int>, n: Int ) -> Bool {
for i in r.startIndex..<r.endIndex {
if arrWithColors[i] == n { return true }
}
return false
}
// here is the main function where i keep getting stuck. Out of let's say five times it would only work 3 or even less.
func randHexWithTag() {
var rNumber : Int = Random(0...5)
for i in 0...5 {
while (true) {
rNumber = Random(0...5)
if !checkForNumberInArrayWithRange(0...5, n: rNumber) {
break
}
}
arrWithColors[i] = rNumber
}
arrWithColors[10] = arrWithColors[1]
for i in 6...11 {
while(true) {
rNumber = Random(0...5)
if ((rNumber == arrWithColors[0] && i == 9) || (rNumber == arrWithColors[2] && i == 11)) {
continue
}
if !checkForNumberInArrayWithRange(6...11, n: rNumber) {
break
}
}
if (i != 10) {
arrWithColors[i] = rNumber
}
}
arrWithColors[13] = arrWithColors[4]
for i in 12...17 {
while(true) {
rNumber = Random(0...5)
if (rNumber == arrWithColors[3] && i == 12) || (rNumber == arrWithColors[5] && i == 14) {
continue
}
if !checkForNumberInArrayWithRange(12...17, n: rNumber) {
break
}
}
if (i != 13) {
arrWithColors[i] = rNumber
}
}
}
The above code will ALWAYS fail during the first call to checkForNumberInArrayWithRange on this line:
if arrWithColors[i] == n { return true }
That is because arrWithColors is empty, and index i is out of range

Resources