Swift: ImageView Aspect Fill errors - ios

I want to create an UI in a UITableView, t's kind like the Facebook Home Page, on the top, there is a image act like a cover image, on the surface of the this "cover image" there is a "user image".
Facebook Home Page
I did this by adding a ImageView first, set the auto layout constrains. and then drag another ImageView on top of previous ImageView, and then add the constrains. the "cover image" has a size of 200 * 600(constrains to the cell with top: 0, left:0, right:0, height: 200), the "user profile image" has a size of 50 * 50(constrains to the cell with bottom:0, left:275, right: 275, hight: 50, width: 50). and the TableView I set as static cell with a hight of 300 and width 600
however, when I set the ImageView mode to "Aspect Fill" the "cover image" will fill the whole cell, even though I set it height as 200. and there is no "user profile image" showing.
anybody know is there anything I did wrong? thanks
updated: in my TableView, I have 2 static cell, the first used to put cover image and profile image, the second one is used to put text content. for the second cell, I want it to be dynamic with the text length, so I use this code:
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row == 0 {
return 300
}
return tableView.contentSize.height
}
I am not sure if this has anything to do with the problem.
I have tried to only set the cover image first, I add the constrains with(top:0, left:0, right:0, bottom: 100) so the cover image should have a size : 600*200 (the cell is 600* 300). but when I set the image view mode to "aspect fill", it will still fill the whole cell, even thought I set the constrains with bottom: 100.

It has something to do with your constraints. Check the priority level on your height constraint. It should be set to 1000.
If you can't figure it out, another option is to put another view under the cover photo with a size of 100 to act as a spacer then constrain the other images to this spacer view. Then you can put the rest of your elements in this view.

Related

How to corrently use UIViews systemLayoutSizeFitting to get the height to show all subviews using a given width?

TD;DR
It seems that in some cases systemLayoutSizeFitting does not return the correct height to correctly show / position all subviews of a view. Am I using systemLayoutSizeFitting wrong or is there some other way to avoid this?
Long story:
The XIB file of a UIViewController does not only contain the main view but also a number of other views which are added to the view controllers view at runtime. All these additional views should get the same height when they are added to the view controllers view.
The views might look like this: A simple container view holding some subviews which are stacked on top of each other.
Since the height of the container view should be flexible, the vertical spacing between the bottom button and the lable above it, uses a grater-than constraint.
To give all views the same height, I tried to measure the necessary height of each view using systemLayoutSizeFitting:
#IBOutlet var pageViews: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
var maxHeight: CGFloat = 0
for pageView in pageViews {
// Add pageView somewhere on view and give it leading, trailing and top
// constraint, but no height constraint yet.
addToView(pageView)
maxHeight = max(maxHeight, pageView.systemLayoutSizeFitting(CGSize(width: view.frame.width, height: UIView.layoutFittingCompressedSize.height), withHorizontalFittingPriority: .required, verticalFittingPriority: .fittingSizeLevel).height)
}
for pageView in pageViews {
// Give all pageViews the same height
pageView.heightAnchor.constraint(equalToConstant: maxHeight).isActive = true
}
}
This does not work, when the label text becomes to long:
In the right example the height is not large enough and thus the button is squeezed. I can counter act this by raising the vertical compression resistance of the button, however in this case the other controls (e.g. the title label) is squeezed...
Why is this? Why does not systemLayoutSizeFitting return a height which is sufficent to show all controls without any squeezing?
Its actually smash button's height when label text is getting bigger . You are setting top and bottom constraints but button height is not declared so when label getting bigger , view basically say "I can reduce button height before updating my height , I have space.Bottom and top constraints are still same , didn't effect."
Giving the constant height constraints of button might be fix your issue.
If you want your view to resist to compression you should use the defaultHigh priority as a verticalFittingPriority instead of fittingSizeLevel.

Append item horizontally into a tableView cell but make them not fill all the space

I have a specific design that I want to achieve, it doesn't seems to complex to me but I'm struggling a lot to make it happen.
First the design looks like this:
So, I have a table view of items which have a picture, then a title and then a collection of three items which will be ImageViews.
All of this inside a cell and inside a stackView.
I tried to make an horizontal stack view and did manage to append my items correctly but it went horribly wrong in terms of design as my imageviews were stretching all the way horizontally. I guess this is not possible to NOT stretch this items.
I also tried to add a collection view into the table view but figured out that was very complex for a thing this basic (as I guess it should be). And then, I'm here.
Here is my code and where I'm stuck at:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = debateList.dequeueReusableCell(withIdentifier: "itemsBox", for: indexPath) as! ItemBox
// Make a variable with 3 participants
let participants = tableView[indexPath.row].participants?.prefix(3)
// iterate over them and adding them to anything that can be doable
participants?.forEach { participant in
let imageView = UIImageView()
let imageUrl = URL(string: participant.imageURL)
imageView.kf.setImage(with: imageUrl)
// Here add item to something
}
return cell
}
I'm stuck and it's getting too long for something this little. Guess I'm a little bit upset with myself for not figuring out how to do it.
If you are having up to 5 participants, then I would suggest you to add by default 2 subviews in horizontal stack view:
empty view with width constraint >= 5 (call it spaceview)
fixed width for "60 % no" UILabel.
then programmatically insert image views with fixed width and aspect ratio 1:1 in horizontal stack view at index 0.
This way, if there will be only 1-2 participants, then the remaining space will be occupied by spaceview as its width will be greater than 5.
If the participants will be more than 5, then better option is to use collectionview with uilabel in horizontal stack view.
First if they are 3 static number of images then it's better to make them inside design and assign the image urls to their outlets respectively
Second for your current work you need to add a width constraint (regarding height they will fit all the stack height when it's a horizontal stack ) like
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.widthAnchor.constraint(equalToConstant: 50).isActive = true

Self-sizing UICollectionView cells with async image loading

I have a horizontal UICollectionView in which I display images that are loaded asynchronously. The cells are supposed to have their width fit the image.
In viewDidLoad of my view controller, I set the estimated cell size:
(collectionView.collectionViewLayout as? UICollectionViewFlowLayout)?.estimatedItemSize = CGSize(width: 400, height: 400)
In cellForItem, I start the download task using Kingfisher:
cell.imageView.kf.setImage(with: url) { (_, _, _, _) in
cell.layoutSubviews()
}
Inside my cell, I have the following code inside the layoutSubviews method:
// 326 is the image view's height
// .aspectRatio is a custom extension that returns: size.width / size.height
imageViewWidthConstraint.constant = 326 * image.aspectRatio
layoutIfNeeded()
In the storyboard, I have properly setup the layout constraints so that imageViewWidthConstraint is respected for the cell's width.
The following is the result when running my app:
As you can see, the cells have a width of 400, although the image was loaded and the layout updated. And, as result, the images are stretched to fill the image view.
When I scroll to the right & then back, the cells are removed from the collection view and loaded back in, and are now properly laid out:
While scrolling, the cells adjust their width, sometimes to the correct width, sometimes to the wrong one.
What am I doing wrong here?
Since your images come in asynchronously it may take some time to be loaded. Only once they are loaded you can actually know the size or ratio of the image. That means for every image that is loaded you need to "reload" your layout. From a short search this looks promising.
At least this way you may get some animations, otherwise your cells will just keep jumping when images start to be loaded.
In any case I would advise you to avoid this. If I may assume; you are getting some data from server from which you use delivered URLs to download the images and show them on the collection view. The best approach (if possible) is to request that the API is extended so that you receive dimensions of images as well. So instead of
{
id: 1,
image: "https://..."
}
You could have
{
id: 1,
image: {
url: "https://...",
width: 100,
height: 100
}
}
You can now use these values to generate aspect ratio width/height before you download the images.
Next to that I don't really see any good solution for the whole thing to look nice (without jumping around).

Auto Layout how to move the view2 from right to the bottom?

I have two views in the cell at the left UIImageView and at the right UIView Containing some other views as in picture
Now I want if the width of Cell is => 300 then View2 should be at the right of Screen as it is in picture other wise it should be moved to the bottom of View1
Do one thing. Set another constraints for that view
1) leading to main view
2) top to imageview (you can give this constraint by right click from view to drag imageview then vertical spacing.)
Now give outlets for this two constraints programatically.
And also give outlets for constraints which are:
1) leading from view to imageview
2) top of view.
Now as per your requirement,set if condition
e.g.
if(width => 300)
{
topOfViewToImageviewConstraint.isActive = false
leadingOfViewToMainViewConstraint.isActive = false
}
else
{
leadingOfViewToImageViewConstraint.isActive = false
topOfviewToMainView.isActive = false
leadingOfViewToMainViewConstraint.constant = 0
topOfViewToImageviewConstraint.constant = 20
}
This will work. I am not currently in Xcode, so this is just some sample code, but the idea will work.
in cellForRowAt
check if self.tableView.cellForRowAt(IndexPath.row).frame.width => 300
if it is, make no change, but if it is not find the y coordinate of the bottom of view 1 and make a new rect at (x: 0, y: botOfView1, width, height). Then set the frame of view 2 equal to that.

Full width view does not contain same frame width as parent

I'm having an issue where I have a UIImageView that spans the entire width of the screen using the following constraints.
Align Leading to : Superview (Equals: -20)
Align Trailing to : Superview (Equals: -20)
Top Space to : Top Layout Guide (Equals: 0)
Aspect Ratio : 1:1
The image assumes the width of the screen while gaining aspect ratio (in this case a square). The issue comes with when I started attempting to make a circle out of the image, that I noticed that my UIIMageView's frames do not have proper values.
You'll have to excuse to code below for not being in Objective-C as I'm using Java through RoboVM but with very little effort you can mentally convert the code to Objective-C as it's pretty straightforward.
public class MyViewController extends UIViewController {
#IBOutlet private UIImageView myImageView;
#Override public void viewDidLoad() {
System.out.println("Image view width: " + myImageView.getFrame().getWidth());
System.out.println("Superview width: " + this.getView().getFrame().getWidth());
}
}
The results of running the application with this Controller shows the following:
Image view width: 240.0
Superview width: 320.0
When cutting the frame width of the image in half and applying that to the CALayer#cornerRadius property, the image does not make a complete circle, however when using half of the superviews width (Which is actually the size of the image) the result is a perfect circle.
In viewDidLoad, it has just loaded your view from the XIB or storyboard, not sized it yet. The size should be correct when viewWillAppear is called. So try putting your code in viewWillAppear and see if that helps.
The reason you need the -20 margin is because your superview has a margin of 20, and the constraints are created by default to 'Relative to margin' - if you uncheck that, you can set the 'real' constraint - in this case zero rather than -20
If you select the constraint in the assistant editor, you will see the check box to toggle this value

Resources