I'm having trouble figuring out how to lay out views in Swift Playgrounds for iPad, though this may also be relevant to Mac users.
The following code should create a view with a red square (also a view) that is near the edges of its' super view, but not touching them.
let v = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
let sqv = UIView(frame: CGRect(x: 400, y: 400, width: 50, height: 50))
sqv.backgroundColor = .red
v.addSubview(sqv)
PlaygroundPage.current.liveView = v
The result is not what you'd expect:
I suspect I know what is going on here; live views are at a fixed size that is larger than the display area. Some characteristics of the view are ignored when it is acting as the live view. However, I can't find where this is mentioned in the documentation, which vexes me. More importantly, how do I deal with this? I would like to be able to layout simple UIs that change to fit the current size of the live view. I don't know how to address this issue without trial & error and hardcoding, which are two things I would really like to avoid.
I suspect I know what is going on here; live views are at a fixed size that is larger than the display area.
Actually it's more like the other way around. An iPad screen is 1024 points wide (in landscape orientation). The right-hand pane (where it shows your live view) is 512 points wide. The playground forces your root view (v) to fill that pane, inset by 40 points on the left, right, and top (and more on the bottom). So your root view's width is forced to 432 ( = 512 - 2 * 40), less than the 500 you specified.
Views created in code (like yours) have translatesAutoresizingMaskIntoConstraints = true, and a resizing mask of 0, which means don't adjust the view's frame at all when its parent is resized. So the playground resizes your root view to width 432, but your root view doesn't move or resize its subview (sqv).
The easiest fix is to set the autoresizing mask of the subview to express your intent that it remain near the right and bottom edges of the root view. That means it should have flexible top and left margins:
let v = UIView(frame: CGRect(x: 0, y: 0, width: 500, height: 500))
let sqv = UIView(frame: CGRect(x: 400, y: 400, width: 50, height: 50))
sqv.autoresizingMask = [.flexibleLeftMargin, .flexibleTopMargin]
sqv.backgroundColor = .red
v.addSubview(sqv)
PlaygroundPage.current.liveView = v
Result:
let sqv = UIView(frame: CGRect(x: UIScreen.mainScreen().bounds.width-50-1, y:400, width: 50, height: 50))
The above code places your subview 1 point away from the right of the main view. Try changing the value 1 after 50 in x to desired value.
Related
I have two apps, one with a UILabel and one using SpriteKit with a SKLabelNode. I'd like to add a black outline around the white text.
I can't find any outline or border properties or anything like that within swift. I've tried just creating new labels with slightly bigger, black font behind them but that didn't look right at all.
Here is my game with SpriteKit
title.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
title.text = "Tap to start!"
title.fontName = "Arial"
title.zPosition = 10
title.fontSize = 50
self.addChild(title)
And here is my other one that uses UILables (It's within a red rectangle so it's easier to see)
let rectangle = UIView(frame:CGRect(x: self.view.frame.size.width / 2 - 150, y: self.view.frame.size.height / 2 - 75, width: 300, height: 150))
rectangle.backgroundColor = UIColor.red
self.view.addSubview(rectangle)
let label = UILabel(frame: CGRect(x: rectangle.frame.size.width / 2 - 75, y: rectangle.frame.size.height / 2 - 10, width: 150, height: 20))
label.textAlignment = .center
label.text = "Tap to Start!"
label.textColor = UIColor.white
label.font = UIFont(name: "Arial", size: 20)
rectangle.addSubview(label)
How do I outline these labels?
For SKLabelNode's, there is no 'easy' way of outlining text. There is however a 'hack'. It involves adding 8 additional duplicate nodes around the label that are coloured black (or whatever colour you want the outline to be), have a zPosition less than the original and otherwise, be identical.
It's important that each label has the same font size as the original.
Your attempt at doing this involved increasing the font size of your one outline copy label. As you said in the question, it doesn't work. You have to use multiple labels so the effect appears seamlessly.
You would place one copy directly above the original, one directly below, one to the left, one to the right, then one on each corner (top right, top left, bottom right, and bottom left). This gives the effect of an outline, however is not efficient and should be taken with a grain of salt.
Note: Keep the amount shifted from the original consistent for all the copies, so the 'outline' is evenly sized.
This should work for UILabels too, however I think there might be an easier way to do this with UILabels.
This question already has answers here:
Why is UIScrollView leaving space on top and does not scroll to the bottom
(13 answers)
Closed 6 years ago.
I am trying to implement a UIScrollView in a similar fashion to the featured banner at the top of the App Store. I am adding 3 views and then paging through them using the code below. When the controller loads however, it is started with the content down a bit. If I tap on the view the content goes back into where it should be. How can I fix this? I've trying setting the content offset to 0, I've tried manually scrolling to the origin rect, and I've tried putting my views in a content view, but nothing has worked.
featuredScrollView.alwaysBounceVertical = false
featuredScrollView.contentSize = CGSize(width: 3 * featuredScrollView.frame.size.width, height: featuredScrollView.frame.size.height)
let contentView = UIView(frame: CGRect(x: 0, y: 0, width: 3 * featuredScrollView.frame.size.width, height: featuredScrollView.frame.size.height))
featuredScrollView.addSubview(contentView)
for i in 0..<3
{
let testView = UIView(frame: CGRect(x: CGFloat(i) * featuredScrollView.bounds.size.width, y: 0, width: featuredScrollView.bounds.size.width, height: featuredScrollView.bounds.size.height))
testView.backgroundColor = .blue
testView.layer.borderColor = UIColor.white.cgColor
testView.layer.borderWidth = 1
contentView.addSubview(testView)
}
Did you try set automaticallyAdjustsScrollViewInsets to NO?
I am just testing some animation of "falling" views. I want to simulate leafs that fall from the sky. I have made this really simple code which is unfinished, but was used to check if I was doing it right.
However, should the "falling" views have auto layout constraints attached to them? Is it bad to just add views to the screen like this when the rest of the view is done with auto layout? I am using Swift 2 and iOS 9.
let rectangleView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 20))
rectangleView.backgroundColor = UIColor.redColor()
backgroundView.addSubview(rectangleView)
UIView.animateWithDuration(10, delay: 0, options: [UIViewAnimationOptions.CurveEaseIn], animations: {
rectangleView.center = CGPoint(x: rectangleView.center.x, y: 300)
}, completion: nil)
If your views are moving freely on the screen, then I don't see any reason to use auto layout constraints.
There is no issue adding a subview to a UI that uses auto layout and animating it yourself, as you are doing. Not everything on the screen has to be managed by auto layout.
This may be too basic or require rephrasing. I am in the process of learning Swift and iOS programming and have developed a basic application that runs successfully on my iPhone 5. The app consists of a label, a button, and a UIImageView. It looks the way I want it to on my iPhone 5.
I figured most of this out by just playing around and so I am creating all these elements programatically. The code looks like this:
let banner = UILabel(frame: CGRect(x: 10, y: 10, width: 300.0, height: 75.0))
let button = UIButton(frame: CGRect(x: 45, y: 75, width: 235.0, height: 60.0))
let imageView = UIImageView(frame: CGRect(x: 22, y: 150, width: 280.0, height: 410.0))
And then I configure them in viewDidLoad to make them show stuff.
Now the question...how to I make them all the right size when running on different devices? I can load up the app on my iPad Mini but it's all scrunched over to the left of the view. So I need to do some kind of dynamic layout but not sure where to start.
All help appreciated!
Rather than creating the view sizes explicitly with initWithFrame: constructors, you can programmatically create and NSLayoutConstraints to your views to automatically layout your views, the same as if you used Auto Layout with the Interface Builder. See Apple's Auto Layout Guide for more details.
I have an image called sampleImage, and I am trying to stretch it along x and y axis, as follows when it is in landscape and it did not work at all
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
if (UIDevice.current.orientation == UIDeviceOrientation.portrait)
{
sampleImage.frame = CGRect(x: 0, y: 0, width: 220, height: 120)
}
else
{
sampleImage.frame = CGRect(x: 0.2, y: 0.2, width: 220, height: 120)
}
}
Then, I did in viewDidLayoutSubviews() as follows,
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if (UIDevice.current.orientation == UIDeviceOrientation.portrait)
{
sampleImage.frame = CGRect(x: 0, y: 0, width: 220, height: 120)
}
else
{
sampleImage.frame = CGRect(x: 0.2, y: 0.2, width: 220, height: 120)
}
}
This works if the device is in landscape, but once you rotate it to portrait and back, all the changes are gone.How to make sure that those dimensions stay same on rotation?
Basically, it's all about constraints. First approach to solve your problem is to handle device rotation (good so answer) and change it manually via setNeedsUpdateConstratints method (another good so answer). This way good for heavy screen with complex changes with UI and constraints. In your case you better use specific constant for constraint base on device orientation that can be set in storyboard.
So, follow this steps:
Set Equal Heights and Equal Widths constraints to the UIImageView, then double click for Width Equals: 120 constraint:
Open View as: iPhone ... bottom bar:
Select specific device and landscape orientation at right side and hit the + before Constant at the right panel.
It will automatically set right Width and Height base on selected device and orientation, so you just need to enter the constant for this case. Run the project and see the result.
Portrait:
Landscape:
Note, also don't forget that different devices may have different size classes (compact x compact, compact x regular, etc.), so maybe you will need to add few more constants to handle this problem (see the table at the bottom of the article).
Finally, you solved the problem by working with storyboard, not programmatically. Pros of that - you code not grow up and not responsible for the UI things, cons - you should add the constant to every size class you want to use.