On my storyboard I have 2 image views.
One holds the main image, the second is an overlay which will be used to specify the crop area of the main image.
In my viewDidLoad I've done this.
let screen_width = UIScreen.mainScreen().bounds.width
let screen_height = UIScreen.mainScreen().bounds.height
self.overlayImage.frame = CGRect(x: self.imageView.frame.origin.x, y: self.imageView.frame.origin.y, width: screen_width, height: (screen_height * 0.1919))
The goal is to have the overlayImage's top left corner lined up properly with the imageView's top left corner. Also the height of the overlay should be about 1/5th of the screens size. However when I run the code the overlayImage is exactly the same size and in the same location it was originally on the storyboard.
How can I programmatically line it up on top of the imageView after the image has been set to it, and also resize the overlayImage dynamically in the viewDidload?
I'd just do it manually in storyboard editor but everyone will have different screen sizes so I thought it best to use the mainscreen().bounds.height variable to determine the amount of height to use dynamically at runtime.
All you have to do is to write your above code in viewDidAppear instead of viewDidLoad as below:-
override func viewDidAppear(animated: Bool) {
let screen_width = UIScreen.mainScreen().bounds.size.width
let screen_height = UIScreen.mainScreen().bounds.size.height
self.overlayImage.frame = CGRect(x: 0, y: 0, width: screen_width, height:screen_height/5)
}
And your frame will set itself as desired.
Since you're working with Storyboard, I would rather use Autolayout and Constraints to solve this problem for me... For instance you set the constraints for your first image view, then set the constraints of your overlay based and related to the former one. Example:
Lower UIImageView: set leading, trailing, top and bottom constraints
Overlay: set center X and Y constraints to be equal to the lower Image
Overlay: set height and width to be proportional to the lower image
Thus, when ever the screen size changes, the two images will always resize equally...
Interface builder:
Set overlay top, leading, width to your image view.
give your overlay a constant height constraint; the constraint cosntant value is arbitrary because you will replace it at run time
ctrl + drag an IBOutlet from this height constraint to your viewcontroller
in your viewWillLayoutSubViews set the .constant of your height constraint to UIScreen.main.bounds.size.height / 5
Related
Hi I want to create a view which looks something like below -
What I tried so far is I put two properties called image and text inside an UIView and tried to initialise the image view and text field and added in UIView I am able to see the image but not the text. May be I am missing proper constraints to put or something else. Can please someone help on what should be the best approach for this.
I would prefer it to be done without much involvement of storyboard i.e setting constraints for different views using storyboard. I am fine setting constraints in code. As you see it is a fairly reusable element and hence I want it as an class which can be assigned to any uiview and that should be it. Please correct me if something can be done better?
Thanks & Regards
Assumptions: the entire picture provided above is contained in a UIView and we are using Storyboard.
Refer to this picture for clarity in the explanation.
Make sure the outermost View has enough constraits so AutoLayout can properly size the view. If this doesn't happen, nothing past this point will matter.
Create 2 Views. Each has the same width as the container and is half the height. Place one in the upper half and one in the lower half. These are the ones with the blue and orange backgrounds.
Add an ImageView to the top View (blue background). Make the ImageView half the height of its Superview. Make the ImageView centered vertically in container. Add a constraint for the leading edge of the ImageView to be the same as the leading edge of the Superview margin. Add an AspectRatio constraint to the ImageView of 1:1.
Add a TextField to the top View (blue background). Center the TextField vertically in the container. Add a constraint for the HorizontalSpace between TextField leading and ImageView trailing and make the constant 8. Add a constraint for the TrailingSpace to the TextView for the SuperView trailing margin.
Change the Placeholder Text for the TextField to "Email"
Place the image in the imageview.
Repeat for the orange background View.
For the bottom borders, use this:
extension UIView{
func addBottomBorder(borderThickness: CGFloat, color: UIColor , widthPct: CGFloat) {
let border = UIView()
border.backgroundColor = color
border.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
var x: CGFloat = 0
let width = self.frame.size.width * widthPct
if widthPct < 1{
x = (self.frame.size.width - width) / 2
}
border.frame = CGRect(x: x, y: self.frame.size.height - borderThickness, width: width, height: borderThickness);
self.addSubview(border)
}
}
I have a static image with an area on it that is meant to display some multi-line text. Like a speech bubble from a character in a video game. I grabbed a stock image that looks like this:
I have a UIImageView set up to be aspect-fit (see this question) inside of a subview of the main view. I have a UILabel also set up inside of the subview, which will hold the multi-line text. I want to be able to move the subview around the screen and have it be any size and still have the UIImageView stay the same aspect and have the UILabel fit inside the bubble of the image.
I made a sample project which has this set up already.
The way I intend to keep the UILabel's bounds inside the speech bubble area is by setting up constraints that are proportional to the center x and y of the UIImageView. For my image, the left edge multiplier is 0.65, the right is 1.8, the top is 0.19 and the bottom is 0.63.
I wrote a couple functions that extend from UIView to confirm this:
/**
Draws a vertical line proportional to the center x of the view.
A `proportional` value of 0 is the left edge, while 2 is the right edge.
:param: proportion The value from the left edge (0.0) to the right edge (2.0)
:param: inColor The color to draw the line in (red by default)
*/
func drawVertLineAtProportion(proportion: CGFloat, inColor: UIColor = UIColor.redColor()) {
let size = self.frame.size
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
let x = self.bounds.origin.x + proportion*self.frame.size.width/2
var vertPath = UIBezierPath()
vertPath.lineWidth = size.height/150.0
vertPath.moveToPoint(CGPointMake(x, 0))
vertPath.addLineToPoint(CGPointMake(x, self.frame.size.height))
inColor.setStroke()
vertPath.stroke()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.addSubview(UIImageView(image: image))
}
And drawHorizLineAtProportion is similar, but for horizontal lines.
I can confirm that my multiplier values are correct by running these 4 lines in viewDidAppear:
imageView.drawVertLineAtProportion(0.65)
imageView.drawVertLineAtProportion(1.8)
imageView.drawHorizLineAtProportion(0.19)
imageView.drawHorizLineAtProportion(0.63)
And then the imageView looks like this:
I can change the size of the subView that contains the imageView to any size I want, and the imageView stays aspect-fit and the red box in the intersection of those red lines is always exactly what I want.
So how come when I set up the constraints of the edges of the UILabel to follow the same formula the edges don't line up?
It seems that when the width of the imageView is maxed the left and right edges are correct but the top and bottom are wrong:
And if the height is maxed, then the top and bottom are correct, but the left and right are wrong:
So how come the UILabel bounds aren't lining up with the red lines?
EDIT to specify that I know there is a runtime fix, but I want to know why storyboard doesn't work.
If I add these lines in viewDidAppear to fix the frame of the UILabel, it works:
let upperLeft: CGPoint = CGPointMake(imageView.frame.origin.x + 0.65*imageView.frame.size.width/2, imageView.frame.origin.y + 0.19*imageView.frame.size.height/2)
let lowerRight: CGPoint = CGPointMake(imageView.frame.origin.x + 1.8*imageView.frame.size.width/2, imageView.frame.origin.y + 0.63*imageView.frame.size.height/2)
let size: CGSize = CGSizeMake(lowerRight.x - upperLeft.x, lowerRight.y - upperLeft.y)
speechLabel.frame = CGRectMake(upperLeft.x, upperLeft.y, size.width, size.height)
But I still want to know why what I have set in storyboard doesn't work.
I figured out a solution, fully in storyboard, that gets me what I want, although I do believe my original question shows that there is a bug in Xcode.
I was noticing that since Xcode didn't seem to respect that I wanted to align speechLabel to the sister-UI element imageView, I just made the UIView I was aligning to no longer the imageView.
The solution I implemented was to put the imageView and speechLabel into a new parent view, which I call aspectFitView. All of the constraints that I put on the imageView to make it aspect-fit, I put on aspectFitView instead.
Then I set the constraints of imageView to make it the size of aspectFitView: align center x and y and equal width and height.
Now that the parent of speechLabel is the exact size, position, and aspect ratio of the imageView, I set up my proportional constraints for the speechLabel against the aspectFitView instead, and voila. It all works beautifully. It looks correct on my storyboard, I don't have to add any post-viewDidAppear frame-correcting code, and it works perfectly on any device I run it on.
I updated the sample project (Xcode 6.4) with the solution for anyone interested.
I have my storyboard set to 4.7" while I work. When I run my app on the iP6 simulator my circular UIImageView looks nice and round. Like so:
However, I'm experiencing distortion with my circular UIImageView when I run the app in all the other simulators
Here's iP5:
and here's iP6+:
Here's the code i'm using to round the UIImageView:
profileImageView.layer.cornerRadius = profileImageView.frame.size.width / 2
profileImageView.clipsToBounds = true
The size of the UIImageView is adjusting correctly, just the roundness gets all funky as you can see. Here are the constraints I have set just in case:
1:1 ratio, align center x to view, align top to view = 15, align bottom to view = -100
The image starts as a perfect square, so I figured that with the 1:1 ratio set it would always stay circular with the code I used...what am I doing wrong here? Thanks so much!
I had this problem before and your code is 100% correct, the problem is with the autolayout and these constraints:
align top to view = 15, align bottom to view = -100
You can't align to top view and to bottom view because the screen size changes. What you can do is only align to top and find another constraint to maintain the size. What works for me with profile pictures is having a size constraint.
Before, with up and bottom constraints:
After, with up and fixed width constraints:
Where did you put that code?
The cornerRadius will not change when the view size changes. You need to update it (to match the view's size) in viewDidLayoutSubviews in your VC, or at the end of layoutSubviews in your view class.
try this definitely it will work.
swift code
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
perform(#selector(self.setCircleForImage(_:)), with: pickedImage, afterDelay: 0)
}
#objc func setCircleForImage(_ imageView : UIImageView){
imageView.layer.cornerRadius = pickedImage.frame.size.width/2
imageView.clipsToBounds = true
}
UIImage must maintain 1:1 aspect ratio
I feel like this is a softball for you veterans, but I'm having a lot of trouble resizing the subview of my UIImageView... here is what I got:
var myImageView:UIImageView!
var tmpView:UIImageView!
myImageView is my "main" UIImageView, and I'm treating tmpView as a subview... now I've tried both with and without auto layout on, but I've set the constraints of myImageView s.t. myImageView takes up the whole screen. I've confirmed this to be true by setting myImageView.image = UIImage...etc.
Here is my code for initializing the image and adding it to the subview:
self.tmpView.image = UIImage(named: "myImage.png")
self.tmpView.frame = CGRect(x: 0, y: 0, width: 200, height: 300)
self.imageView2.addSubview(self.tmpView)
Now here is where I am running into problems - no matter what I set tmpView's height and width to, the size never changes. Interestingly though, changing x,y does have an effect on the position of the subview.
Here are my questions: 1) Why do both x and y obey nicely, but width and height do not?
2) How do I fix this, and is it best to do so programmatically or through the storyboard?
You should no longer modify the frames directly when using autolayout. Add a width and height constraint to tmpView, and create outlets for them if you're using IB (The outlets should have the type NSLayoutContraint).
You can also create the constraints in code but there's no point of doing it that way unless you have to.
In both cases, to resize tmpView, change the constant property of each of the constraints to the values you want.
widthConstraint.constant = yourNewWidth;
heightConstraint.constant = yourNewHeight;
Looking to fill the background image in Xcode iOS based on a percentage.
The Constraints
The image must fill the entire screen (centered), so that the smallest dimension (width or height) fits. (Even if image becomes pixelated because it has a small file size.)
This must work universal, both iPad and iPhone devices.
Examples
You can use UIImageView. To make an image fill the screen, set imageView.contentMode to ScaleAspectFill.
To make UIImageView fill the screen, use auto layout. The easiest way is to add 4 constraints for UIImageView. Spacing to nearest neighbor = 0 will do. Like so:
Or leading/trailing/top/bottom space to superview = 0, if you like.
Set height and width of your image view to the width and height of your screen
var image:UIImageView = UIImageView(frame: CGRectMake(0, 0, self.view.frame.width, self.view.frame.height))
You want to make a UIImageView with the contentMode set to .ScaleAspectFill and add it as the bottom subview of your view.