Set font size equal to an integer? - ios

In Swift, I have a line that says var timerFontSize = 85. I want to have the line timerLabel.font = UIFont(name: "HelveticaNeue-Ultralight", size: timerFontSize), although that doesn't work. It only allows me to type in a number, not assign it to a variable. The reason I don't want to just type in a number is because I have a timer, where every second the font variable drops by 1.
How can I set the font size equal to an integer?

simply type cast the number to CGFloat
UIFont(name: "HelveticaNeue-Ultralight", size: CGFloat(timerFontSize))

Using this line:
var timerFontSize = 85
You're setting your variable implicitly to type Int, while the method for creating a font takes a CGFloat argument.
You could cast this variable as a CGFloat when you call the method, or you could explicitly create the variable as a CGFloat type:
var timerFontSize: CGFloat = 85
It's worth noting that
var timerFontSize = 85.0
Creates timerFontSize as a double, and
var timerFontSize = 85.0f
creates timerFontSize as a float.
The former will work for 64-bit devices where CGFloat ends up being a double, and the latter will work for 32-bit devices where CGFloat ends up being a float. But you don't want to use either of these options as it will crash on the other device.
CGFloat is a typedef for float/double depending on whether the device is 32 or 64 bit processor.

Related

Why does the compiler claim CGRect has no width member?

Note that I'm not trying to set the value in a CGRect. I'm mystified as to why the compiler is issuing this claim:
let widthFactor = 0.8
let oldWidth = wholeFrameView.frame.width
let newWidth = wholeFrameView.frame.width * widthFactor // Value of type '(CGRect) -> CGRect' has no member 'width'
let newWidth2 = wholeFrameView.frame.width * 0.8 // This is fine.
Width is a CGFloat where your multiplier is a Double. Explicitly declare the type of your multiplier:
let widthFactor: CGFloat = 0.8
All the dimensions of a CGRect are of type CGFloat, not Double, and because Swift is especially strict about types, you can't multiply a CGFloat by a Double.
The interesting thing though, is that both CGFloat and Double implement ExpressibleByFloatLiteral. This means that 0.8, a "float literal", can be interpreted as either a Double or a CGFloat. Without context, it's always a Double, because of how the compiler is designed. Note that this only applies to float literals like 3.14, 3e8 etc, and not to identifiers of variables.
So the expression wholeFrameView.frame.width * 0.8 is valid because the compiler sees that width is a CGFloat, so it treats 0.8 as a CGFloat as well. No problems.
On the other hand, when you declare the variable widthFactor, it is automatically given the type Double, because there aren't any more context on that line to suggest to the compiler that you want it to be any other type.
This can be fixed by directly telling the compiler that you want widthFactor to be a CGFloat:
let widthFactor: CGFloat = 0.8
Because, as others have noted, you can't multiply a Double and a CGFloat, the compiler doesn't know what you're intending.
So, instead of giving you an error about the frame property, which you currently think it's doing, it's actually making its best guess*, and giving you an error related to the frame method. No method method has a width property, so what it tells you is true.
*Of course, its best guess is not good enough, hence a human being coming here to ask a question about it. So please file a bug!
Stepping onto my soapbox: This confusion would be avoided if Apple hadn't named the method the thing it returns. The convention to prefix all such methods with get solves the problem. Some convention is important in any language with first-class functions, to disambiguate between properties and methods.
wholeFrameView.frame has no member width. Also, you need widthFactor to be of type CGFloat. Try:
let newWidth = wholeFrameView.frame.size.width * CGFloat(widthFactor)

Swift Enum design issues : 1. multiple cases having same value, 2. returning custom enum value

I'm maintaining an enum with the majority of font sizes used in my app like so -
enum FontSize : CGFloat
{
case HeaderSize = 20
case TitleSize = 18
case PrimaryButtonTextSize = 22
}
I have written this convenience method in a UIFont extension that I wish to use across the app like -
static func fontWithSize(size:FontSize) -> UIFont?
{
return font = UIFont(name:"System" , size: size.rawValue)
}
USAGE -
headerLabel.font = UIFont.fontWithSize(.HeaderSize)
Now I'm facing 2 issues with this design -
#1
Since I'm identifying font sizes by where they are used in UI I would most likely add more cases in the future as -
enum FontSize : CGFloat
{
case HeaderSize = 20
case TitleSize = 18
case PrimaryButtonTextSize = 22
// Newly added
case SpecialButtonTextSize = 20
case SpecialTitleTextSize = 10
}
The problem is that swift enum will not allow me to add multiple cases with same value. Of course I could just remove SecondaryButtonTextSize or SpecialTitleTextSize and always use .HeaderSize -
specialButton.font = UIFont.fontWithSize(.HeaderSize)
specialTitle.font = UIFont.fontWithSize(.HeaderSize)
But this would mean that if I decide to change HeaderSize later I'd be missing out on my special button and special title cases. Also call me crazy but I'd be happier if I could use those enums like that.
#2
Now of course this enum will not be an exhaustive list of all font sizes my app uses. There'll be those one-off cases where I'll have to pass in a size value for a one time use only.
One solution is to just have another method like -
static func fontWithSize(size:CGFloat) -> UIFont?
{
return font = UIFont(name:"System" , size: size)
}
But It'd be awesome if somehow I could continue using my enum and make it return a custom value. maybe it'd look something like
enum FontSize : CGFloat
{
case HeaderSize = 20
case TitleSize = 18
case PrimaryButtonTextSize = 22
case CustomSize(CGFloat) -> CGFloat // lolwut?
}
I know this might be a trivial thing to worry about, but it'd be great to have some design solution for this issue. Maybe enums aren't the solution at all! Any pointers will help!
Your enum have init(rawValue: CGFloat) initializer. If you'll use the same rawValue for different cases, it won't be able to decide which case produce from this value. So it's no surprise compiler doesn't allow to do that.
Solution to your problem is simple: get rid of cases and use static vars instead:
enum FontSize {
static var HeaderSize: CGFloat = 20.0
static var SpecialButtonTextSize: CGFloat = 20.0
static var SpecialTitleTextSize: CGFloat = 10.0
}

Swift ScrollView - contentSize can't take a variable Int

Trying to set a scrollView's contentSize and I've run across this issue (Xcode 6.4)...
Both of these work perfectly:
scrollView.contentSize = CGSize(width:self.view.frame.width, height:1000)
scrollView.contentSize = CGSizeMake(self.view.frame.width, 1000)
Once a let (or var) gets involved, these do not work:
let testing = 1000
scrollView.contentSize = CGSize(width:self.view.frame.width, height:testing)
Error: Cannot find an initializer for type 'CGSize' that accepts an argument list of type '(width: CGFloat, height: Int)'
let testing = 1000
scrollView.contentSize = CGSizeMake(self.view.frame.width, testing)
Error: Cannot invoke 'CGSizeMake' with an argument list of type '(CGFloat, Int)'
Change the let statement to the following:
let testing:CGFloat = 1000
You need to do this as the CGSizeMake function requires two parameters of the same type, so you can either make both ints or make them both CGFloats. In this case it is probably easier to use testing as a CGFloat in the first place. In other cases, you might want to try something like
let testing = 1000
scrollView.contentSize = CGSizeMake(Int(self.view.frame.width), testing)
Or:
let testing = 1000
scrollView.contentSize = CGSizeMake(self.view.frame.width, CGFloat(testing))
So that both are of the same type.
A number literal doesn't have an explicit type. Its type is inferred at the point that it's evaluated by the compiler.
A number variable must have an explicit type. The default type is Int
let testing : CGFloat = 1000.0
scrollView.contentSize = CGSize(width:self.view.frame.width, height:testing)

Swift error: "double is not convertible to UInt8"

var dvalue:Double = 1.03
var stok1 = SKSpriteNode(imageNamed: "stok")
stok1.zRotation = dvalue * stok1.zRotation
This piece of code gives this error in the last line:
"Double is not convertible to UInt8"
How to fix this?
zRotation is a CGFloat.
Your dValue is a Double.
They are not compatible. To multiply them, they must be same type: either both CGFloat or both Double. In this case, clearly CGFloat is desirable, since we will be assigning the result to a CGFloat.
Declare your dValue as a CGFloat, therefore, and all will be will.

What am I doing wrong with Swift and basic geometry

When I use the hard coded data for diameter and height Swift runs simulator fine, but when I try and use text values in place using TextField.text.toInt() then I keep getting very annoying error message of:-
cannot invoke \ with an argument list of type $st15
Just where am I going wrong. I am new to Swift and have only used AppInventor before to create an app with a few thousand downloads. I am a enthusiastic but probably slow learner, but I will get there if someone would be so kind to help me out a little. Note: Formula is simply PIr2 x h to give cylinder volume. I want to use diameter which explains why I am halving each time.
let PI = 3.142
var bodyDiameter = bodyDiameterTextField.text.toInt() // 3.0
var bodyHeight = bodyHeightTextField.text.toInt() // 10.0
var cylinderVolume: Double
var cylinderVolume = (PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume")
cylinderVolumeLabel.text = "(cylinderVolume)"
Here follows same code with hard coded values for Bodydiameter and Bodyheight. It all works great in the playground and the simulator. I guess its got something to do with Integers and Floats, but I'm probably way out.
let PI = 3.142
var bodyDiameter = 3.0
var bodyHeight = 10.0
var cylinderVolume = (PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume")
cylinderVolumeLabel.text = "(cylinderVolume)"
Your code has several errors.
First, and not really an error, there's already a built-in constant for π, named M_PI. Use that instead of defining your own PI constant.
Next, String.toInt() returns an Int?. The question mark means the return type is really Optional<Int>. This is a container that is either empty (nil), or contains an Int. If you want to use the Int value, you need to unwrap it. You might want to check that the Optional isn't nil first, though.
Next, assuming you unwrap the Int, you can't perform arithmetic on mixed Int and Double values in Swift. You have to convert to all Int or all Double. You probably want to use all Double. In fact, you probably don't want to convert from Int to Double at all. You probably want to get a Double from the text field in the first place. There's no toDouble on String in Swift, but there are some other ways to do it.
Finally, you need to say \(cylinderVolume) to interpolate the value into the string. Your code omits the \.
Try this:
var bodyDiameter = (bodyDiameterTextField.text as NSString).doubleValue
var bodyHeight = (bodyHeightTextField.text as NSString).doubleValue
var cylinderVolume = (M_PI * (bodyDiameter / 2.0) * (bodyDiameter / 2.0)) * bodyHeight
println("cylinderVolume: \(cylinderVolume)")
cylinderVolumeLabel.text = "(cylinderVolume)"
If you want to convert the strings to Doubles in a localization-friendly way, or detect when the strings aren't valid Doubles, look up NSNumberFormatter.

Resources