Swift ios viewDidLoad, viewDidAppear image control size different - ios

i have noticed that an UiImageView that's set to take full width is not given its correct width before viewDidAppear.
This is a bit problmatic for me because:
I load in a custom logo graphic that can have any high/width
This means that to keep scale (and full width) I set a height constraint to match width/height proportions in the .png file
However, I can not get true image width before viewDidAppear - nor viewWillAppear
Am I missing a stage / tehcnique here (new to xcode/ios/swift) or will I have o maybe make my own calcluations and no rely on retrieving the width of an uiimageview?

keep in mind that viewDidLoad is called only when your view is loaded and not yet put on a superview, so you still don't know how is it's final frame, it has just the frame you defined in its xib file (if any)
but then you add it in a superview (in many way) and so (following its autoresizingMask) its frame size changes, and you can get them only in
viewDidAppear method

Related

iOS incorrect frame size at runtime

I've got an UIImageView inside a root view of a controller, and I've set it to be 90% of the screen width and given it an aspect ratio as constraints to set the dimensions.
In the code I'm trying to do something with respect to the size of the UIImageView at runtime, however when I get the frame.size.height or frame.size.width of the UIImageView they are clearly wrong and way too small.
At first I was accessing the size in viewDidLoad(), after which I found quite a few posts suggesting to do it either in viewWillLayoutSubviews(), viewDidLayoutSubviews(), or viewWillAppear(). Unfortunately I've tried all of those and none of these contexts seem to provide the right value. I suspect it may have something to do with auto layout but I'm not quite sure how to get around this. Any insight as to why this may be would be appreciated
viewDidLoad is too early. At this time, the views have the frames they were given in the storyboard. Ditto for viewWillAppear.
In viewWillLayoutSubviews, the view controller's top-level view has its correct frame, but its descendants do not.
In viewDidLayoutSubviews, the view controller's immediate subviews have their correct frames, but more distant descendants (“grandchildren” and so forth) don't.
If the image view is a direct subview of the view controller's view, then its frame is up to date in viewDidLayoutSubviews.
If the image view is a more distant descendant, then there is no method you can override in the view controller that will be called after the image view's frame has been updated but before the image view is visible on screen. Here are two options in this case:
Create a custom subclass of UIView to be the superview of the image view. When the superview's layoutSubviews runs, after it calls super.layoutSubviews, the image view's frame is up to date.
Create a hidden UIView that is a direct subview of the view controller's top-level view. Use constraints to make this hidden view's frame exactly match the image view's frame. Hidden views participate in layout, so when viewDidLayoutSubviews is called, this hidden view's frame is up to date, and is the same as the image view's frame will eventually be (except that the hidden view's frame is in the top-level view's geometry, which might be different than the geometry of the image view's superview).

How to calculate UIImageView size?

I have a Swift app which sets a UIImageView to the size of a portion of the screen (set with autoconstraints to the margins and controls above it), and I use the height and width values of the ImageView to create 3 additional UIImageViews programmatically and position them on top of the original image. I am constructing a podium and place three images on top of the podium in appropriate positions. I then calculate the height and width of each additional image by creating a constant: let conImageHeight = 0.4 * self.imgPodium.frame.height. However, I can't get a reliable height and width of the original image.
Initially I used self.imgPodium.bounds.height which worked great for the iPhone 6 simulator screen size, but when I tried running the app on an iPad simulator, it kept the iPhone size values. The image itself changed size but when I println() the height and width using bounds, I get the values of the iPhone image.
I then changed and used self.imgPodium.frame.height but this didn't change anything.
Is there a better way to get the height and width of a UIImageView (or any control for that matter) after it positions itself through autoconstraints?
Thanks.
Questions I've looked at already but didn't help:
Change UIImageView size and position programmatically in Swift
how to check screen size of iphone 4 and iphone 5 programmatically in swift
UIImageView Bounds, Frame
Cocoa: What's the difference between the frame and the bounds?
How to resize an UIImageView (swift)
In viewDidLoad the subviews have been loaded and initialized but not layed out yet. Therefore their frame property for example is not valid yet and may be the one that was set in Xcode when designing the UI in the first place - that however is not reliable. You should do custom layout in places where you can be sure the layout has already happened yet: e.g in viewWillLayoutSubviews or viewDidLayoutSubviews. Both methods will also get called when the layout somehow needs to change. In viewDidAppear the layout has been done yet and therefore you can do some layouting too. This code however will get not get called when the layout changes due to interface rotation for example. AND it will get called after a presented viewController gets dismissed again. (not what you would want).
In both cases: do your setup of creating new imageViews in viewDidLoad and do the layout in viewWillLayoutSubviews or viewDidLayoutSubviews!

Why is frame width of the view alway 600 by 600 with autolayout

I am making a basic Minesweeper app for practice / fun in swift. I want to make it so the size of the board (10 tiles wide) adapts to any iOS screen.
To do this I'm setting the size of each tile by getting my tileContainer view.frame.width and / 10.
My problem is that the tileContainer view is set to 600, no matter what. On my Storyboard I set the tileContainer equal to the width of the View Container, by CTR click & dragging to the view and selecting equal width. This keeps the width of tileContainer set at 600, regardless of the device I'm testing on. (which is my problem, width should change depending on screen width not a constant 600)
Does anyone know how I can get the proper width of the screen regardless of the device it's being used on?
When you are using auto-layout, the subviews are laid out after the viewDidLayoutSubviews function. Therefore, if you call tileContainer.frame.size before that, such as in viewDidLoad, it will always be 600 by 600 (which is the default size in storyboard).
viewDidLayoutSubviews: Called to notify the view controller that its view has just laid out its subviews. reference
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print(tileContainer.frame.size); // This is actual size you are looking for
}
Short answer: UIScreen.mainScreen().bounds.size.width always returns the width of the screen.
Long answer: It sounds like you are using auto layout with size classes. You can make this all work just using constraints. You can set a proportional width and height constraint from your tile to your container so that the tile is 10% of the width and height of the container. Then, you can set all the other tiles to have equal width and height to that one tile. Then, use constraints to position them in a grid.
Another strategy using autolayout would be to set the spacing between the cells to be 0 and leave the width and height unconstrained. If you have 10 cells with 0 space between the cells and eachother, and 0 space between the front and back cells and the container, then they will automatically take on the 1/10 of the width of the container.
Additional note, if using size classes/auto layout, the view size is not properly set until subviews are laid out, so if you are trying to do this width stuff in viewDidLoad, for example, the width would still be the width of the Any Any size class (600).
While the accepted answers is a correct answer I thought I might at least say that you can force the view to draw itself before hitting viewDidAppear:.
Either, you can in your viewDidLoad: call:
view.setNeedsLayout()
view.layoutIfNeeded()
Or if you create it from "outside":
let vc = UIViewController()
let _ = vc.view
or
let vc = UIViewController()
vc.view.setNeedsLayout()
vc.view.layoutIfNeeded()

Incorrect frame being reported using Autolayout & Size Classes

I've laid out a UIView in my UIViewController. Constrained its height and width, and centered it vertically and horizontally with constraints, but when I log its frame, it reports a location that seems appropriate to the full sizeClass, but not the size of the screen I'm looking at.
For example the default size class UIViewController has a width of 600px, and in that size, my UIView is 150px from the left edge. When I check this distance from the left edge while running in the iphone5 simulator, it still reports 150px.
The weird thing about all of this, is that the UIView itself still appears in the expected (adjusted for screen-size) location.
For posterity: the problem I was having -- getting incorrect values when printing the frame of a view -- was caused by accessing that frame during ViewDidLoad. At this point in the ViewController lifecycle all views may have not been laid out.
Therefore, it is safest to access a UIView property/outlet only after ViewDidLayoutSubviews has been called.
I mean, it's right there in the name of the method: "Your views have been laid out... now do stuff with them."

issues in resizing UIImage on IB

I am facing a strange issue while resizing images in IB (Xcode 5). First, I drag & drop a UIImageView on my viewController in a storyBoard. I then resize and set an image for it, which works properly. However, once I try to resize the UIImageView control after setting the image, it starts behaving strangely. Sometimes it occupies the complete viewController, other times the image becomes so big that it is out of bounds.
I tried to set a smaller image, but it resulted in more strange behavior. Now if I try to resize it, sometimes it can't be resized by dragging. Sometimes the UIImageView is resizes properly but the contained image remains the same size. Any suggestions?
This was not happening when using Xcode 4.
There are two issues here.
For an image to be resized to the same size as its image view, it is essential that its content mode be set to UIViewContentModeScaleToFill, UIViewContentModeScaleAspectFit, or UIViewContentModeScaleAspectFill. You can make this setting in the attributes inspector.
NOTE: If you do not do this, then the image will be shown at its own natural size. If the image is huge, it will be shown huge. Moreover, it will be shown huge even if the image view is small, because the image view does not clip its image. If you want to the image view to clip its image (so that you can see where the bounds of the image view really are), set the image view's Clips To Bounds (clipsToBounds). Again, you can make this setting in the attributes inspector.
In Xcode 5, you are using auto layout by default. This means that the image view has the size of its image by default. If you don't want that, you must use constraints to set the height and width of the image view. You will then probably also have to use constraints to set its position. Do NOT run the app until all auto layout warnings in Interface Builder have been taken care of, or you will get a big surprise at runtime.

Resources