Why are UIScreen.bounds incorrect in iOS11 - swift

Please don't mark as duplicate. Available threads haven't provided an answer. Behavior is iOS11 only.
Updating a project from Xcode 8 to Xcode 9, using now iOS11 but still Swift 3, I have the following experience:
print("UIScreen.main.bounds.width = \(UIScreen.main.bounds.width)")
print("self.view.frame.width = \(self.view.frame.width)")
let rect = CGRect(
x: 0,
y: 0,
width: UIScreen.main.bounds.width,
height: UIScreen.main.bounds.height * 0.25
)
prints:
UIScreen.main.bounds.width = 414.0
self.view.frame.width = 600.0
The view is supposed to be from screen edge to screen edge. Therefore I have used UIScreen.main.bounds.width. But the value far too small for the actual view controller size of self.view.frame.width.
Why is that? What am I missing? Help is very appreciated.
Physical device. Same behavior using UIScreen.main.bounds.width

I had a similar problem with incorrect fetching of UIScreen.main.bounds size. The size was less than a physical screen. The solution was to add the next line into info.plst:
Key:
Launch screen interface file base name
Type:
String
Value:
LaunchScreen
So check your info.plist to see if it contains this line. If it's not, just add it. Hope this helps.
screen shot of info.plst

Related

Bottom Border for UITextField in Swift

I found this question with a solution to my problem but it still has some misbehavior when I'm using it.
How to only show bottom border of UITextField in Swift
In my Project it seems like the bottom border doesn't know the correct width of my UITextField and is longer than my UITextField. The UITextField has constraints to the SaveArea on the left and right and a constraint to the text above. I would say that the width is defined by the constraints at run time so that the code in the extension should be able to get the correct width. But the way my code is getting the width seems to be wrong. Can someone help me?
Thanks in advance :)
I know you found a workaround but just thought I might add a fix for this if you still were considering using this.
So it seems that your set up was fine but it works a little bit differently on different device sizes.
self.frame.size.width in the extension is not always returning back the correct width of the UITextField in some devices and on orientation changes.
So to get this to work, here are the small changes I made:
extension UITextField
{
func addBottomBorder() {
let bottomLine = CALayer()
// The border should be inside the text field
// so I changed frame.size.height + 5 to
// frame.size.height - 1
bottomLine.frame = CGRect(x: 0,
y: frame.size.height-1,
width: frame.size.width,
height: 1)
bottomLine.backgroundColor = UIColor.black.cgColor
borderStyle = .none
layer.addSublayer(bottomLine)
// Add this so the layer does not go beyond the
// bounds of the text field
layer.masksToBounds = true
}
}
After this, the result should be as you hope:

How to vertically align text of NSSecureTextField in swift for macOS [duplicate]

I have been reading through the various options on how to set the vertical alignment on an NSTextField. I want the text to be displayed in the center and to do it programatically in Swift. Here are the things I have looked so far:
http://www.cocoabuilder.com/archive/cocoa/174994-repositioning-an-nstextfieldcell.html
https://red-sweater.com/blog/148/what-a-difference-a-cell-makes
Vertically Centre Text in NSSecureTextField with subclassing
Get NSTextField contents to scale
vertically align text in a CATextLayer?
One thing I have tried in Swift is to set the following property:
textField.usesSingleLineMode = true
Any tips on the best way to vertically center text would be much appreciated!
This is very hard to do, as Apple makes this very difficult. I achieved it by subclassing NSTextFieldCell and overriding the drawingRectForBounds: method like so:
override func drawingRectForBounds(theRect: NSRect) -> NSRect {
let newRect = NSRect(x: 0, y: (theRect.size.height - 22) / 2, width: theRect.size.width, height: 22)
return super.drawingRectForBounds(newRect)
}
This is just my way to do it, I'm sure there are better ways, which I don't know (yet). And this only works for the standard font size in TextFields (which gives a text height of 22). That's why I hardcoded that. Haven't figured out yet, how to get the height in the cell if you change the font.
Result:
Try this on a playground, it centers the text perfectly, use it on your projects! Hope it helps!
import Cocoa
let cell = NSTableCellView()
cell.frame = NSRect(x: 0, y: 0, width: 100, height: 100)
let tf = NSTextField()
tf.frame = cell.frame
tf.stringValue = "MyTextfield"
tf.alignment = .Center
let stringHeight: CGFloat = tf.attributedStringValue.size().height
let frame = tf.frame
var titleRect: NSRect = tf.cell!.titleRectForBounds(frame)
titleRect.size.height = stringHeight + ( stringHeight - (tf.font!.ascender + tf.font!.descender ) )
titleRect.origin.y = frame.size.height / 2 - tf.lastBaselineOffsetFromBottom - tf.font!.xHeight / 2
tf.frame = titleRect
cell.addSubview(tf)
I have added the NSTextField inside a NSView and centered it.
Another solution was (in an iOS project) to create a UILabel and allow it adjust its size (sizeToFit()) and again embed it inside a UIView.
I personally don't like the calculations in previous answers and the second solution for iOS works for all texts size and row numbers.
I was also facing vertical alignment issue with NSTextField. My requirement involved, rendering a single-line string inside a NSTextField. Additionally,
textfield needed to be resize implying we had programatically resized the font-point-size of the text inside text-field on resize. In this scenario we faced vertical-alignment issues - the mis-alignment was tough to grasp/understand in a straight forward way.
What finally worked:
So, in my scenario a simple,
turn off the "Single Line Mode" in interface builder
for the text-field solved the issue.
The accepted answer works perfectly and here's the Swift3 version.
class VerticallyAlignedTextFieldCell: NSTextFieldCell {
override func drawingRect(forBounds rect: NSRect) -> NSRect {
let newRect = NSRect(x: 0, y: (rect.size.height - 22) / 2, width: rect.size.width, height: 22)
return super.drawingRect(forBounds: newRect)
}
}

UIScreen.main.bounds.width returning nil in Swift 3

In my project, I got messed up with two versions of app. I propably copied the project file and it created a problem. This line
myLogo.frame.size.width = UIScreen.main.bounds.width
shows, that UIScreen.main.bounds.width returns nil. How is that even possible?
fatal error: unexpectedly found nil while unwrapping an Optional value
What could be the reason and how can I repair it?
I'm working on the latest Xcode and writing in Swift 3.
Be sure myLogo is connected to your storyboard (the little dot to the left of myLogo's declaration should be dark). Then set it's frame's width property like this:
let width = UIScreen.main.bounds.width
let r = myLogo.frame
myLogo.frame = CGRect(x: r.origin.x, y: r.origin.y, width: width, height: r.height)
This would probably be better to do in Interface Builder with auto-layout so myLogo's width is constrained to the device's width.
Here's a useful extension that lets you adjust individual properties of a UIView's frame e.g.
myLogo.width = UIScreen.main.bounds.width

Can't stack views correctly in a NSClipView

I'm getting mad at this code because it just shows the last view (in it's correct position) and it forgets to paint the others...
// Gets a stack of devices
let volumes = getDevices()
var index = 0
for device in volumes {
let volume = devicePrototype
// devicePrototype it's a custom view
volume?.setName(name: device.name) // setting the label for the name
volume?.setIcon(icon: device.icon) // setting the image for the icon
// Setting the position of the view (width = 600, height = 105)
// and all the views have the same size.
volume?.layer?.frame = CGRect(x: 0, y: index * 105, width: 600, height: 105)
// Adding the view to the NSClipView (Here's the problem :P)
devicesStack.addSubview(volume!)
index += 1
}
Can you help me find the problem please?
I think you are creating only one view (device) and modifying the same through the loop. You are referencing same devicePrototype object repeatedly and changing the properties only. Please check that.
Try to initialize a new object in each iteration,
let volume = DevicePrototype()

How to properly display a HUD on a SpriteKit game across different resolutions?

I originally created my game with iOS 8 and tested on my iPhone 6S. The game looks fine in the 5, 5S, 6, 6 Plus, 6S, and 6S Plus (since all devices have the same ratio of 16:9). As you can see from the image, the music button is offset from the top right corner. The image is offset by this code:
muteButton.position = CGPoint(x: CGRectGetMidX(self.frame) + 920, y: CGRectGetMidY(self.frame) + 480)
The problem I have is if someone tried this game on an iPad, it will display this. As you can see, the bottom graphic and the mute button are offset from the sides by a lot.
I want to make it so that the objects will always stay close to the sides of the frame/view. Making the app "universal" on xCode does not fix it either. Or do I just make a completely new project built for the iPad?
Don't forget about the 4s, you will get the same problems as iPad. SpriteKit does not have constraints like in the UI builder, so you are going to have to accommodate for the 4:3 and the 16:9 devices by applying some math, or force the 4:3 to be 16:9 with black borders using the .AspectFit scaling method.
Now I am not sure where 920, and 480 are coming from, but those numbers may have to be tweaked in this code when detecting the device. Simplest way to determine your aspect ration is to do UIScreen.mainScreen().bounds.width/UIScreen.mainScreen().bounds.height, then work from there.
Solution! I figured it out! For those who come from the future and also might need help with this. This works with landscape orientation and portrait orientation.
Note: You must have your scene.scaleMode set to .AspectFill for this to work on all scenes and the scene size has to be 2048x1536 or 1536x2048. This will make it scaleable for iPad too.
I have declared the following variable on the top of my class.
class StartScene: SKScene {
let playableArea: CGRect
}
Then, I have the following code inside the override init() function.
override init(size: CGSize) {
//1. Get the aspect ratio of the device
let deviceWidth = UIScreen.mainScreen().bounds.width
let deviceHeight = UIScreen.mainScreen().bounds.height
let maxAspectRatio: CGFloat = deviceWidth / deviceHeight
//2. For landscape orientation, use this*****
let playableHeight = size.width / maxAspectRatio
let playableMargin = (size.height - playableHeight) / 2.0
playableArea = CGRect(x: 0, y: playableMargin, width: size.width, height: playableHeight)
//3. For portrait orientation, use this*****
let playableWidth = size.height / maxAspectRatio
let playableMargin = (size.width - playableWidth) / 2.0
playableArea = CGRect(x: playableMargin, y: 0, width: playableWidth, height: size.height)
super.init(size: size)
}
From here, I then use the variable playableArea to position my objects.
titleChild.position = CGPoint(x: CGRectGetMidX(playableArea), y: CGRectGetMaxY(playableArea) - (titleChild.size.height * 0.90))
Works amazing. Looks good in the iPhone 4S, 5, 5S, 6, 6 Plus, 6S, 6S Plus, and iPads.
If you want to see the box in the app to make sure you did it right, use the following function.
func drawPlayableArea() {
let shape = SKShapeNode()
let path = CGPathCreateMutable()
CGPathAddRect(path, nil, playableArea)
shape.path = path
shape.strokeColor = SKColor.redColor()
shape.lineWidth = 8
addChild(shape)
}
Then just call the function in the didMoveToView() function to view the red frame to make sure you did the code right. This will create a red frame the size of the view that is viewable to the user. Now that you have playableArea to hold the frame that the user can see, you can use it for other things such as making sure objects don't or can't leave the bounds, etc. For this screenshot, I use it to prevent the user from moving the spaceship outside the device.