I am a swift newbie. I am studying iOS Programming The Big Nerd Ranch Guide 4th Edition. The code in the book is in objc. I am translating them to swift. The following code from page 150 does not work for me. It throws EXC_BAD_INSTRUCTION. Could anybody please point where I am wrong? Thank you for your help.
func drawHypnoticMessage(message: NSString) {
for i in 0..20 {
let messageLabel = UILabel()
messageLabel.backgroundColor = UIColor.clearColor()
messageLabel.textColor = UIColor.whiteColor()
messageLabel.text = message // Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP,subcode0x0)
messageLabel.sizeToFit()
let width = self.view.bounds.size.width - messageLabel.bounds.size.width
let x = Int(arc4random()) % Int(width)
let height = (self.view.bounds.size.height - messageLabel.bounds.size.height)
let y = Int(arc4random()) % Int(height)
var frame = messageLabel.frame
frame.origin = CGPointMake(CGFloat(x), CGFloat(y))
messageLabel.frame = frame
self.view.addSubview(messageLabel)
}
}
NB: I have tried replacing
messageLabel.text = message
with
messageLabel.text = "text"
UPDATE 1:
I found the solution
func drawHypnoticMessage(message: NSString) {
for i in 0..<20 {
let messageLabel = UILabel()
messageLabel.backgroundColor = UIColor.clearColor()
messageLabel.textColor = UIColor.whiteColor()
messageLabel.text = message
messageLabel.sizeToFit()
let width = self.view.bounds.size.width - messageLabel.bounds.size.width
let x = Int(arc4random() % UInt32(width))
let height = (self.view.bounds.size.height - messageLabel.bounds.size.height)
let y = Int(arc4random() % UInt32(height))
var frame = messageLabel.frame
frame.origin = CGPointMake(CGFloat(x), CGFloat(y))
messageLabel.frame = frame
self.view.addSubview(messageLabel)
}
}
Basically, I change let x = Int(arc4random()) % Int(width)
to let x = Int(arc4random() % UInt32(width))
. Does anybody know why it work?
UPDATE 2:
Latest revision using arc4random_uniform
:
func drawHypnoticMessage(message: NSString) {
for i in 0..<20 {
let messageLabel = UILabel()
messageLabel.backgroundColor = UIColor.clearColor()
messageLabel.textColor = UIColor.whiteColor()
messageLabel.text = message
messageLabel.sizeToFit()
let width = self.view.bounds.size.width - messageLabel.bounds.size.width
let x = CGFloat(arc4random_uniform(UInt32(width)))
let height = (self.view.bounds.size.height - messageLabel.bounds.size.height)
let y = CGFloat(arc4random_uniform(UInt32(height)))
var frame = messageLabel.frame
frame.origin = CGPointMake(x, y)
messageLabel.frame = frame
self.view.addSubview(messageLabel)
}
}
Judging by the fix you provided, I think the problem is that arc4random
returns a 32 bit unsigned integer between 0 and 2 32 - 1. You were then converting it to an Int
which (I believe) is a 32 bit signed integer in iOS (32 bit mode), so half your random numbers (those between 2 31 and 2 32 - 1) are not expressible as an Int
.
In the new code, you limit the random number to either 0 to width
or 0 to height
brefore you try to convert it to Int
which is why it doesn't crash.
NB. Rather than using %
you should use arc4random_uniform() . Taking the mod of a random number introduces a bias, that is avoided with arc4random_uniform().
Have you tried to change "let messageLabel" to "var messageLabel"?
Edit: I want to correct myself. In this case, "let messageLabel" is the right way to declare the UILabel. "let" is for constants, but when declaring "let messageLabel = UILabel()," we are saying that the pointer to UILabel is constant, not that UILabel can't be changed after declaration.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.