10

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?

kdopen
  • 8,032
  • 7
  • 44
  • 52
Evan Nudd
  • 216
  • 2
  • 10
  • "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 sounds like the right way to do already. – Yuchen May 14 '15 at 02:57
  • Visually it works perfectly, sets the view width properly in the visual sense, but the call in code to do tileContainer.frame.width will always return 600. If I'm testing on iPhone 6, resizable iPhone, iPhone 5s. Always returns a 600 – Evan Nudd May 14 '15 at 03:02
  • And because it's at 600, the tiles widths are set to 60, and they quickly end up going offscreen – Evan Nudd May 14 '15 at 03:07

3 Answers3

21

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
}
Yuchen
  • 30,852
  • 26
  • 164
  • 234
  • Thanks that makes sense and answered my question. Do you know a function that will be called or a way to wait to execute code after everything is loaded in? Because viewDidLayoutSubviews() is being called twice for me. – Evan Nudd May 14 '15 at 03:45
  • Hello @EvanNudd, yes, this function will get called multiple times. See this [post](http://stackoverflow.com/questions/27014360/why-viewdidlayoutsubviews-call-multiple-times) for an explanation. But I think it is alright. For example, when you rotate your device, the width will definitely changed, and you will need to change your tiles accordingly. – Yuchen May 14 '15 at 03:52
  • 1
    I guess you are trying to create some subviews as tiles? If that is the case, what you can do is to put the alloc init code in the viewDidLoad and then put some repositioning/resizing code in the `viewDidLayoutSubviews`. – Yuchen May 14 '15 at 03:54
  • 1
    Another suggestion would be go with constraint. You can set constant in code as well. The HUGE benefits of using auto layout is that you don't have to worry about the layout for device orientation, different device screen size and etc. You can learn more about using auto layout programmatically [here](https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutinCode/AutoLayoutinCode.html). – Yuchen May 14 '15 at 03:56
  • thank you! this saved a lot of time ( after frantic search for this issue ). I had tired using self.myScrollView.frame.width for getting scrollview width so that i can set it to image view. It always returned 600, even on lower phone resolutions. Now it works perfect. – kishorer747 Jun 13 '16 at 10:44
  • just one thing, i can still see my image view is a very big one, after 1 or 2 sec, the image gets resized to the scroll view. any idea why? Case is I navigate to a new page, the image is being set and dimes are still 600, but after a slight delay, it adjusts itself. – kishorer747 Jun 13 '16 at 10:45
  • @kishorer747, nice to know that it helps (at lease part of it). The reason why you see the UI glitches is probably because the view will get layout first, and then you change the size of the image, and then it gets layout again. If you need to have the size of the image to be exactly the same size of your scroll view. Why do you need the scroll view at all? – Yuchen Jun 13 '16 at 13:39
  • @YuchenZhong i need a image slider. the way i have implemented now is i have a scrollview. I keep adding images to it horizontally and user can scroll through them, also there is a paging indicator to indicate on which ```page``` the user is. – kishorer747 Jun 13 '16 at 13:50
  • @kishorer747 Instead of implementing this paging behavior from scratch, maybe consider using [UIPageViewController](https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/) – Yuchen Jun 13 '16 at 14:05
  • @kishorer747 if you really want to use scroll view. My suggestion is that don't add the first image from storyboard, add it programmatically after view did appear. – Yuchen Jun 13 '16 at 14:07
  • @YuchenZhong yes, i am doing that only. I am just creating a placeholder scroll view in story board. And programmatically adding image views. – kishorer747 Jun 14 '16 at 12:26
  • @kishorer747 then I don't understand why you would see the image being very big and shrink down. Maybe it is too much to continue on the comments. Could you post a separate question with some relevant code and screenshot? – Yuchen Jun 14 '16 at 12:34
  • 1
    @YuchenZhong i don't know why or how, but the its not happening now. Image is coming properly now – kishorer747 Jun 14 '16 at 12:44
4

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).

Will M.
  • 1,864
  • 17
  • 28
4

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()

user023
  • 1,450
  • 2
  • 17
  • 21