limit input options in init() in swift - ios

I'm making a simple Dice class in swift.
I would like the Dice initializer to be called with the desired amount of eyes/sides the dice should have. I have two variables to set a min and max of number of sides you should be able to give the dice upon init...
However, I'm not quite sure of how to make the init fail if the dice is being initialized with a number outside of this range, when I can't make sue of try/catch in swift.
My code is as follows:
class Dice : SKSpriteNode {
let sides : UInt32
var score : Int
init(sides : Int){
let min_sides = 2
let max_sides = 6
self.sides = UInt32(sides)
self.score = 1
let imageName = "1.png"
let cardTexture = SKTexture(imageNamed: imageName)
super.init(texture: cardTexture, color: nil, size: CGSize(width: 100, height: 100))
userInteractionEnabled = true
}

Use a failable initializer instead. From that you can return a nil value if the condition doesnt satisfied
init?(sides : Int){
if sides > max_sides{
return nil
}
}

Related

Value of type 'Any' has no member 'removeFromSuperview'

Q.1 Why does 'Any' has no member 'removeFromSuperview'?
Q.2 Why does it say cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members?
var allImgViews = [Any]()
viewDidLoad(){
// sample code
for v in 0..<4 {
myImageView.image = UIImage(named: something)
allImgViews.append(myImageView)
}
allImgViews[0].removeFromSuperview()
}
Any help is appreciated
Because Any means literally ANY data type. There's nothing that requires an Any typed value to have to be able to be a view that's capable of being added or removed from a view. Int can be an Any, String can be an Any. What would you expect something like this to do?
let any: Any = 123
any.removeFromSuperView() // What should this call do??? o.0'
Because you only know the members of a value by the type that you know the value has. That's what a type is: it's a categorization of values by what operations you can perform on them.
In a type-safe language like Swift, you can't perform any operation unless your sure the value your doing it to supports that operation. And you gain that certainty when you know the values exact type, because then you can check to see whether that type supports the operation or member you're trying to use.
There's no reason to be using Any here, in the first place. Here's how to write this with nice, specific types:
var imageViews = [UIImageView]()
func viewDidLoad() {
for v in 0..<4 {
let imageView = UIImageView(frame: CGRect(x: 0 , y: 192, width: 98, height: 94))
imageView.image = UIImage(named: something)
imageViews.append(imageView)
}
imageView[0].removeFromSuperview()
}
If imageViews has no values before the append calls in viewDidLoad, this code can be better written as:
let imageViews = [UIImageView]()
func viewDidLoad() {
imageViews = (0..<4).map { v in
let imageView = UIImageView(frame: CGRect(x: 0 , y: 192, width: 98, height: 94))
imageView.image = UIImage(named: something)
return imageView
}
imageView[0].removeFromSuperview()
}

Can I use a Selector with parenthesis in Swift?

I was wondering how can I use a Selector in Swift 3 including a value in the parenthesis that the func requires.
let fireRecogniser = UISwipeGestureRecognizer(target: self, action: Selector(("shootShot")))
^ That is the recogniser I have but the method 'shootShot' has a parameter for an Element which is an enum that I have.
Here is the 'shootShot' function:
func shootShot(type: Element) {
let shot = SKSpriteNode(imageNamed: "\(type)Shot")
shot.texture?.filteringMode = SKTextureFilteringMode.nearest
shot.position = CGPoint(x: -self.frame.width / 2 /*playerframe*/, y: -(self.frame.height / 2) + grnd.frame.height)
shot.setScale(1)
shot.physicsBody = SKPhysicsBody(circleOfRadius: (shot.frame.height / 2))
shot.physicsBody?.affectedByGravity = false
shot.physicsBody?.allowsRotation = true
shot.physicsBody?.isDynamic = true
shot.physicsBody?.restitution = 0
shot.physicsBody?.angularDamping = 0
shot.physicsBody?.linearDamping = 0
shot.physicsBody?.friction = 0
shot.physicsBody?.categoryBitMask = Contact.Shot.rawValue
shot.physicsBody?.contactTestBitMask = Contact.Enemy.rawValue
self.addChild(shot)
// THIS WILL DEPEND ON DIFFICULTY
let spin = SKAction.rotate(byAngle: 1, duration: 0.3)
shot.run(SKAction.repeatForever(spin))
let move = SKAction.moveTo(x: self.frame.width / 2, duration: 3.0)
let remove = SKAction.removeFromParent()
shot.run(SKAction.sequence([move, remove]))
}
As you can see, the method has an Element that the function requires.
Any help as to how I can include that parameter in my Selector?
Thanks. :)
Type your selector like this in Swift 3
let fireRecogniser = UISwipeGestureRecognizer(target: self, action: #selector(shootShot(element:)))
It is possible... if you are the one performing the selector.
There is an overload of perform() that has a with: argument. The argument you pass in the with: argument will be passed to the selector method.
Example:
// in some NSObject subclass's method
perform(#selector(myMethod), with: "Hello")
// in the same class
func myMethod(x: String) {
print(x)
}
If the first line is executed, "Hello" will be printed.
However, in yor case, since you are not the performer of the selector, you can't perform the selector with the arguments you want.
You can workaround this by adding a class level variable that indicates which Element you want to call the method with:
var shotElement: Element!
You can set this to some value before you pass the target and action to the gesture recognizer.
Then, access it in shootShot:
let shot = SKSpriteNode(imageNamed: "\(shotElement)Shot")
I admit this isn't the perfect workaround, but it's the most straightforward.

Allow Users To select Player Skin Found nil on if Statement

I am trying to allow the player to select a skin from a UIViewController than when the sprite is called it is loaded with their selected skin.
ball.swift
var skin : Skins!
init(ballName name : String?, ballColor color : UIColor, ballMass mass : CGFloat, ballPosition pos : CGPoint) {
if skin.dogSkin == true{
super.init(texture: SKTexture(imageNamed: "doge"), color: color, size: CGSize(width: radius * 2, height: radius * 2))
print("was called here too")
}
else{
super.init(texture: SKTexture(imageNamed: "circle"), color: color, size: CGSize(width: radius * 2, height: radius * 2))
}
Skins.swift
var dogSkin = false
#IBAction func backbutton(sender: AnyObject) {
}
#IBAction func dogPressed(sender: AnyObject) {
dogSkin = true
print("I hit it")
}
Now getting fatal error: unexpectedly found nil while unwrapping an optional value,When the if statement is triggered, can't use a guard statment on it.
changed it to
var skin = Skins()
works-- doesn't crash. But doesn't this create a new instance of Skins? Making the BOOL false again?
Get this in the console though? and skin doesn't change.
<xxxx.Skins: 0x7fa59d825b30>
<xxxx.Skins: 0x7fa59d828500>
<xxxx.Skins: 0x7fa59d82a7a0>
<xxxx.Skins: 0x7fa59d82c970>
<xxxx.Skins: 0x7fa59d82e960>
<xxxx.Skinss: 0x7fa59d90d7b0>
<xxxx.Skins: 0x7fa59d90ffb0>
<xxxx.Skins: 0x7fa59d912000>
<xxxx.Skins: 0x7fa59d914060>
...
You made the variable skin an implicitly unwrapped optional variable. That means that any time you reference it, it assumes the variable contains a valid value and crashes if it does not.
You don't assign a value to skin before trying to reference it, so you crash.
Forget you ever knew about the force-unwrap operator ! for your first 2 months of programming in Swift. For newbies, it's the crash operator.

Property-like closures for local variables

I didn't found answer for my question in swiftbook.
Is this possible to create property-like closure for local variable in swift? I mean smt like further snippet:
func someFunc() {
// here goes our closure
var myRect:CGRect {
var x = 10
var y = 20
var width = 30
var heigth = 40
myRect = CGPointMake(x,y,width,heigth)
}
}
I have complexity evaluation of UI elements position. This trick should make my code much readable
This is called read-only computed property where you can omit the getter to simplify declaration:
var myRect: CGRect {
let x:CGFloat = 10
let y:CGFloat = 20
let width:CGFloat = 30
let height:CGFloat = 40
return CGRectMake(x, y, width, height)
}
Read-Only Computed Properties
A computed property with a getter but no setter is known as a
read-only computed property. A read-only computed property always
returns a value, and can be accessed through dot syntax, but cannot be
set to a different value.
NOTE
You must declare computed properties—including read-only computed
properties—as variable properties with the var keyword, because their
value is not fixed. The let keyword is only used for constant
properties, to indicate that their values cannot be changed once they
are set as part of instance initialization.
You can simplify the declaration of a read-only computed property by
removing the get keyword and its braces:
Documentation Swift Conceptual Properties
Why not try this way?
fun someFunc() {
var myRect = {() -> CGRect in
let x:CGFloat = 10.0
let y:CGFloat = 20.0
let width:CGFloat = 30.0
let height:CGFloat = 40.0
return CGRectMake(x,y,width,height)
}
myRect() //Call it
}
EDIT I think if there are some requirements to calculate some points position like maxElement use closure is good to save some small functions.

Lag using runAction with a SKLabelNode on swift

I am having a lag issue with this function that is used a lot of times in my app...
plusOne(scorelabel.position,plus: 1)
And:
func plusOne(position: CGPoint, plus : Int) {
myLabel.setScale(1)
myLabel.text = "+"+String(plus)
myLabel.position = position
myLabel.hidden = false
let action1 = SKAction.scaleTo(2, duration: 0.5)
let action2 = SKAction.fadeOutWithDuration(0.5)
let actionGroup = SKAction.group([action1,action2])
myLabel.runAction(actionGroup,completion: {
self.myLabel.hidden = true
})
}
The first time I use the plusOne function, always make my app be freezed for a little time...
I do not know if I have been doing the things well... myLabel has been declared global but it is the same... always with lag on the first execution.
You need to set the font of your label with a fix font at start.
Like that:
let yourFont = UIFont(name: "yourfontName", size: 17)
var myLabel = SKLabelNode(fontNamed: yourFont?.fontName)
Otherwise, your font gets loaded at the first usage and not on app-start.

Resources