What is the benefit of define constant by closure in Swift 3 - swift

let a: UIView = {
let a = UIView()
a.frame = CGRect(x: 0, y: 0, width: 20, height: 20)
return a
}()
I saw a lot of people's Swift source code defining let as this way. I just curious what is the benefit of this way?

In this case, there is no benefit, but if the variable in question were a value type then the benefit would be that you could perform some mutating setup code and still get a constant out of it.
It also lets you hide temporary variables that were only needed to initialize the constant, since they'll only exist inside the closure's scope.

Related

Why omitting of structure property reports compile-time error

I'm study the swift by the Apple's official book "The Swift Programming Language" and faced a compilation error. I create a structure which has two properties with default values. And attempt to initialise with only one parameter results in a compilation error. What a reason of it?
struct Size {
var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)
let zeroByTwo = Size(height: 2.0)
let zeroByZero = Size()
In Swift whenever we are going to create a structure. Swift automatically creates the default initializer for the particular structure. In your case, you will get a compile-time error in let zeroByTwo = Size(height: 2.0).
Because you are not passing all the parameters required by the default initializer. to fix that issue you can create your own init() functions in your structure as shown below.
struct Size {
var width:Double, height:Double
init(width:Double = 0.0, height:Double = 0.0){
self.width = width
self.height = height
}
}
let twoByTwo = Size(width: 2.0, height: 2.0)
let zeroByTwo = Size(width: 2.0)
let zeroByZero = Size()

Instance member cannot be used on type when adding UIImage Programatically

I'm new to Swift, and I'm still trying to get my head around many things. I made a class for a Meteorite:
class Meteorite {
var width: Int,
height: Int,
x: Int,
y : Int
init(width: Int, height: Int, x: Int, y: Int) {
self.width = width
self.height = height
self.x = x
self.y = y
let image = UIImage(named: "square")
let imageView = UIImageView(image: image!)
imageView.frame = CGRect(x: self.x, y: self.y, width: self.width, height: self.height)
view.addSubview(imageView)
}
}
... And I'm trying to make the meteors appear through UIImage View elements. However, on the line: view.addSubview(imageView)
I keep getting thrown the error:
Instance member 'view' cannot be used on type 'ViewController'
I might be doing this wrong, but this class is defined within the ViewController class, which is a subclass of UIViewController. I can't find anything that makes much sense to my situation online. Help is greatly appreciated :)
Meteorite may be nested within a UIViewController subclass, but you are still referencing a view property within the context of the Meteorite class. Essentially, you are sending a message to a variable that doesn't exist.
The thing to do here is to move any manipulation of the view hierarchy outside of the init for an element. This should be handled by the view controller, not the subview.
I would suggest configuring the Meteorite object in it's init, but adding it to the view controller's view within, say the viewDidLoad: method of the view controller.

viewController does not update

I have 2 UIViewControllers, one starting the GameScene and the other is supposed to show the highscore, as below. The problem is that my second UIViewController does not want to show the udpated highscore. It just keeps on showing 0 all the time.
Does anyone know what the problem is? I tried using viewDidLoad as well as viewWillAppear but none of them work.
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 200, height: 21))
label.center = CGPoint(x: 160, y: 285)
label.textAlignment = .center
label.text = String(GameScene().highScore)
self.view.addSubview(label)
}
in the GameScene, I have declared the highScore as so:
var highScore = Int()
and used in a gameOver function as so:
if score > highScore {highScore = score}
thanks
label.text = String(GameScene().highScore)
This piece of line suggests GameScene is a static class. Un less it is, you simply cannot access variables in such a manner.
You will need to pass data between view controllers to pass data between the scenes. You can pass data using one of three options:
Though performSegue()
performSegueWithIdentifier() and make a connection in IB
make a connection in IB and define an action

Adding New Parameters to a Child Class in Swift

I've been writing a game that has a subclass of the SKSpriteNode with some extra functions and variables. I'd like to set some of the variables when the object is created eg
let mySprite = MySubclass (width: 24, height 33)
I'm not sure this is possible which means I'll probably have to call a methos of the subclass to set the vars in a separate stage which is a bit clunky:
let mySprite = MySubclass ()
mySprite.setSize(24, height: 33)
Any ideas how I can do this in a more elegant way?
Many Thanks,
Kw
This is very fundamental OOP. Here is how you do it in Swift:
class MySubClass: SKSpriteNode {
var width: CGFloat // Declare your own properties
var height: CGFloat // ...
init(width: CGFloat, height: CGFloat) {
self.width = width // set up your own properties
self.height = height // ...
super.init() // call up to the super-class's init to set up its properties
}
}
Have you read Apple's book The Swift Programming Language? It's free and clearly covers this...

missing argument for parameter 'coder' in call Swift

In my code I'm getting:
missing argument for parameter 'coder' in call swift
I'm a beginner to Swift and I've already tried everything including researching this question, but have found no answer. Thanks.
The code I'm getting this error is:
let button: UIButton = UIButton(frame: CGRect(origin: CGPoint(x: ChecklistViewController().view.frame.width / 2 + 117, y: ChecklistViewController().view.frame.size.height - 70), size: CGSize(width: 50, height: 50)))
The error is suggesting that the compiler is having trouble figuring out which init method should be invoked, so it's assuming you meant to call init(coder:).
But let's set that aside for a second. First, let's simplify your statement to eliminate some "noise". Rather than using CGRect(origin:, size:), you might use CGRect(x:, y:, width:, height:). That would yield (separating it on separate lines to make it a little easier to read):
let button = UIButton(frame: CGRect(
x: ChecklistViewController().view.frame.width / 2 + 117,
y: ChecklistViewController().view.frame.size.height - 70,
width: 50,
height: 50)
)
Second, the problem here is that that ChecklistViewController() syntax isn't actually referencing your existing ChecklistViewController. Every time it sees ChecklistViewController() it's creating a new instance of that view controller (so you probably have three instances, the original one and the two you accidentally created here). That's certainly not what you intended. If you were doing this in, one of the instance methods of the view controller itself, you'd just reference self, e.g.:
let button = UIButton(frame: CGRect(
x: self.view.frame.width / 2 + 117,
y: self.view.frame.size.height - 70,
width: 50,
height: 50)
)
A more subtle problem is that this code will only work if the frame of the view has been set. But if you have this code in viewDidLoad, the frame hasn't been set yet. If you did this in viewDidAppear, though, you could get away with this code. Generally, you'd use auto layout, to avoid this, e.g. something like:
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
// do additional configuration of the button here
view.addSubview(button)
NSLayoutConstraint.activateConstraints([
button.centerXAnchor.constraintEqualToAnchor(view.centerXAnchor, constant: 117),
button.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor, constant: -70),
button.widthAnchor.constraintEqualToConstant(50),
button.heightAnchor.constraintEqualToConstant(50)
])
Because we did this with auto layout, it means you can do this in viewDidLoad, if you want. Also, it means that if you rotate the device, the constraints will recalculate the frame automatically for you.
Having said all of this, the "missing argument for parameter 'coder'" might be a result of some other problem in the code. But, if you fix the declaration of the button, you might be able to better diagnose any other issue that might be in the code.
I think the problem is that you are using ChecklistViewController to generate your positions.
Try this code
let button: UIButton = UIButton(frame: CGRect(x: (CGRectGetMidX(self.view.frame) + 117) , y: (CGRectGetMaxY(self.view.frame) - 70) , size: CGSize(width: 50, height: 50)))