iOS Swift Setting View Frame Position Programmatically - ios

I having two sub views inside scrollview. I need to position that both subviews programmatically. I did it correctly by writing code inside DispatchQueue.main.async. Here is the code:
DispatchQueue.main.async {
self.SelectClientDetailView.frame = CGRect(x: 0, y: 637, width: self.SelectClientDetailView.frame.size.width, height: self.SelectClientDetailView.frame.size.height)
self.SelectClientDetailView2.frame = CGRect(x: 0, y: 837, width: self.SelectClientDetailView2.frame.size.width, height: self.SelectClientDetailView2.frame.size.height)
}
Its working good but when I scrolling my scrollview this both views set back to old positions. How to fix it. Its Default y position will be SelectClientDetailView:400 and SelectClientDetailView2: 600

If you are using Auto Layout then setting frame will cause some weird effects. Auto Layout and Frames doesn't go together. You'll need to rearrange the constrains, not the frames. While using Auto Layout changing the frames will cause some weird effects and will eventually revert back to the constraints you've created in the original UIView.
Some solutions:
If you want to use Autolayout approach then, You need to create an outlet to each constrain just like you would to a view and change its constant when needed.
disable Auto Layout in that specific xib and start playing with frames.

If you only want to change the frame Y position, try this instead:
self.SelectClientDetailView.frame.origin.y = 637
self.SelectClientDetailView.frame.origin.y = 837
As already mentioned, you might need to check your view hierarchy to be sure you are actually adding them to the UIScrollView (and not elsewhere).

Related

Increasing height of UIView.frame programmatically works till iPhone 8 plus but doesn't works beyond model iphone x

I am increasing the height for UIView programmatically in runtime on click of button, it works fine on iPhone 6,7,8 and plus models but doesn't works on iPhone X and beyond models. i have initially set constraints programmatically as i views are autoLayout based. I also tried to set constraints at runtime which didn't helped too.
//Tried this and working till iPhone 8+
self.view.frame.size.height += CGFloat(280)
//or
self.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width,
height: self.view.frame.height + 20.0)
//Tried setting height constraint
self.view.autoSetDimension(.height, toSize: 270)
Debug view hierarchy and look for conflicting constraints that might interfere with your height constraint. Also make sure you set translatesAutoresizingMaskIntoConstraints to false if you set constraints in code.
Also: if you set your constraint like this in code (apparently you are using PureLayout), that method returns the reference to that height constraint. So if you want to modify it, you need to update the constant value of your constraint, instead of setting a new one.
For any further help you certainly need to share more information.

SnapKit and Dynamic UITableViewCell not being laid out correctly

I'm currently building a reusable view that takes advantage of the dynamic resizing that UITableView has on offer with its cells.
What I'm trying to achieve is to give a view within the cell a width and a height depending on the size it has been given within the size property that is defined within a struct, I pass this through using a configure function, after setting the size of the view.
I then center it on the xAxis and apply any margins around the view using a property within the struct which is just a UIEdgeInset property. Using the SnapKit library this looks like this.
if let formImageItem = formImageItem,
let size = formImageItem.size {
formItemImgVw = UIImageView(frame: CGRect(x: 0, y: 0, width: size.width, height: size.height))
formItemImgVw?.image = formImageItem.image
formItemImgVw?.contentMode = formImageItem.aspectFit
contentView.addSubview(formItemImgVw!)
formItemImgVw?.snp.makeConstraints({ (make) in
make.size.equalTo(CGSize(width: size.width, height: size.height))
make.top.equalTo(formImageItem.margin.top)
make.bottom.equalTo(formImageItem.margin.bottom)
make.centerX.equalToSuperview()
})
}
The issue I seem to be having is i'm getting the following warning.
2018-10-12 14:57:34.101532+0100 xxxx Dev[6104:2160548] [LayoutConstraints] Unable to simultaneously satisfy constraints.
Probably at least one of the constraints in the following list is one you don't want.
Try this:
(1) look at each constraint and try to figure out which you don't expect;
(2) find the code that added the unwanted constraint or constraints and fix it.
(
"<SnapKit.LayoutConstraint:0x2830ec720#FormImageViewTableViewCell.swift#61 UIImageView:0x10530a940.height == 114.0>",
"<SnapKit.LayoutConstraint:0x2830ec780#FormImageViewTableViewCell.swift#62 UIImageView:0x10530a940.top == UITableViewCellContentView:0x105503500.top>",
"<SnapKit.LayoutConstraint:0x2830ec7e0#FormImageViewTableViewCell.swift#63 UIImageView:0x10530a940.bottom == UITableViewCellContentView:0x105503500.bottom>",
"<NSLayoutConstraint:0x2837e2580 'UIView-Encapsulated-Layout-Height' UITableViewCellContentView:0x105503500.height == 44 (active)>"
)
Will attempt to recover by breaking constraint
<SnapKit.LayoutConstraint:0x2830ec720#FormImageViewTableViewCell.swift#61 UIImageView:0x10530a940.height == 114.0>
I understand what this is telling me basically that it's choosing to use the height 44 that is set by default for a table view cell. But I can't seem to get my head around why it's using this default height rather than the one that I have defined within my constraints and applying the height to the view whilst also adding the spacing to the top and bottom.
Also when using the debugger to inspect the UI it seems like a height isn't being set at all and as you can see the height of 44 is just clipping the entire imageview.
So ultimately what I'm trying to achieve is the ability to give a view a defined height and width, and use values to apply top and bottom spacing(margins) in order to resize the cell that it's currently within.
You need to lower the priority of the bottom constraints so replace this
make.bottom.equalTo(formImageItem.margin.bottom)
with
make.bottom.equalTo(formImageItem.margin.bottom).priority(999)
plus in viewDidLoad
tableView.estimatedRowHeight = 120
tableView.rowHeight = UITableViewAutomaticDimension
abd don't implement heightForRowAt

ios - Adjust button width and height based on screen size

I am trying to adjust button sizes according to the device they are run on. iPhone SE is small compared to iPhone 8 and as a result the buttons do not fully appear.
I tried using the following code to adjust the size of the buttons according to the screen size but it did not show any changes.
roundedCornerDeliveryButton.layer.cornerRadius = 8
roundedCornerKitHomeButton.layer.cornerRadius = 8
widthMultiplier = Double(self.view.frame.size.width) / 69
heightMultiplier = Double(self.view.frame.size.height) / 321
roundedCornerDeliveryButton.frame.size.width = roundedCornerDeliveryButton.frame.width * CGFloat(widthMultiplier)
roundedCornerDeliveryButton.frame.size.height = roundedCornerDeliveryButton.frame.height * CGFloat(heightMultiplier)
roundedCornerKitHomeButton.frame.size.width = roundedCornerKitHomeButton.frame.width * CGFloat(widthMultiplier)
roundedCornerKitHomeButton.frame.size.height = roundedCornerKitHomeButton.frame.height * CGFloat(heightMultiplier)
roundedCornerDeliveryButton.frame.origin = CGPoint(x: roundedCornerDeliveryButton.frame.origin.x * CGFloat(widthMultiplier), y: roundedCornerDeliveryButton.frame.origin.y * CGFloat(heightMultiplier))
roundedCornerKitHomeButton.frame.origin = CGPoint(x: roundedCornerKitHomeButton.frame.origin.x * CGFloat(widthMultiplier), y: roundedCornerKitHomeButton.frame.origin.y * CGFloat(heightMultiplier))
How would I do this?
There are a few ways to do this, but it comes down to how you declared your buttons in the first place.
If your buttons are declared in Storyboard or Xib file, you probably should be using layout constraints.
For example, if you want a button to take 1/3rd, you start by defining a layout constraint with the top view of the view controller with "Equal Width", then you edit that constraint and change its multiplier to 1:3
The layout system will do its magic to ensure the constraints is respected and the button is always 1/3rd the screen width.
You can declare several constraints like that to automatically respect different constraints, like making sure your button height is always taller than 36pt, width is never wider than 400pt, etc. Just have to define the proper priorities and the constraints.
Defining your sizing constraints this way has the advantage of being inspectable in the Xib as you can quickly change device type & orientation and make sure everything works before even running your code.
Good luck!
To make the button fit its content use
button.sizeToFit()
Also it's better to do it with auto-layout
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
])
You can add this constraint if you want it proportionally
button.widthAnchor.constraint(equalTo:self.view.widthAnchor,multiplier:0.75)

Modify height of a container view

Here is my app with the profile on the left (ProfileViewController) and a containerView on the right (ContainerViewController). What I am trying to achieve is once the containerViewController has done its job, it will update the height of the UIView.
// ContainerViewController.swift
let heightConstraint = self.view.constraints.filter { $0.identifier == "Height" }.first
heightConstraint?.constant = height
When browsing the list of constraints, they're all emtpy, and I did set up some constraints in the storyboard. So Why ? Is there any way to access the UIView within the ContainerViewController ?
Ps:
DispatchQueue.main.async {
self.view.frame = CGRect(x: 0, y: 0, width: width, height: height)
}
Sounds to work, but I think that modify constraint is proper ? Nope ?
What I would do, is to set IBOutlet for the constraint, and store it inside ContainerView.
This way, you don't depend on order number or anything else, to get this constraint, even if code (or storyboard) will changed in the future.
And Yes, the right way is to set the constraint constant, and not changing frame.
But be aware, that even constraint constant change need to be put on the main (UI) thread - (by the look of your code, I assume, you are dealing with the threads).

How to add leading padding to view added inside an UIStackView

This is my setup: I have an UIScrollView with leading,top, trialing edge set to 0. Inside this I add an UIStackView with this constraints:
stackView.centerYAnchor.constraintEqualToAnchor(selectedContactsScrollView.centerYAnchor).active = true
stackView.leadingAnchor.constraintEqualToAnchor(selectedContactsScrollView.leadingAnchor).active = true
Inside the stack view I add some views.
My issue is that because of the constraints the first view added to stack view will also have leading edge = 0.
What are the ways that I could add some padding to the first view ? Without adjusting the scroll view constraints.
When isLayoutMarginsRelativeArrangement property is true, the stack view will layout its arranged views relative to its layout margins.
stackView.layoutMargins = UIEdgeInsets(top: 0, left: 20, bottom: 0, right: 20)
stackView.isLayoutMarginsRelativeArrangement = true
But it affects all arranged views inside to the stack view. If you want this padding for only one arranged view, you need to use nested UIStackView
I have found that constraints don't work inside a Stack View, or they seem somewhat strange.
(Such as if I add leading/trailing constraint to selected on image stackview, that adds leading to collectionview too, but doesn't add trailing; and it be conflict for sure).
To set layout margins for all views inside the stackview, select:
Stack View > Size Inspector > Layout Margins > Fixed
Note: "Fixed" option was formerly called "Explicit", as seen in the screenshots.
Then add your padding:
The solution you have provided doesn't add a padding for your views inside your UIStackView (as you wanted in the question), but it adds a leading for the UIStackView.
A solution could be to add another UIStackView inside your original UIStackView and give the leading to this new UIStackVIew. Then, add your views to this new UIStackView.
Hint, you can do that completely using Interface Builder. In other words, no need to write code for it.
What worked for me is to add to stack view another UIView that's just a spacer (works at least with stackView.distribution = .Fill):
let spacerView = UIView(frame: CGRect(x: 0, y: 0, width: 10, height: 10))
stackView.addArrangedSubview(spacerView)
stackView.addArrangedSubview(viewThatNeedsSpaceBeforeIt)
stackView.addArrangedSubview(NextView)...
If you only need leading padding, then you can set the stack view's Alignment to "Trailing" and then you will be free to specify unique Leading constraints on each of its contained subviews.
As a bonus, you can also set the stack view's alignment to "Center" and then you can use Leading and/or Trailing constraints to give each item its own padding on both sides.
Set your stackview alignment to "center". After that you can give every subview different leading and trailing.
swift 3:
You just need set offset by:
firstView.leadingAnchor.constraint(equalTo: parentView.leadingAnchor, constant: 200).isActive = true
Be sure this constraint set after you parentView.addArrangdSubView(firstView)
The solution would be to have a regular view in the stack view to hold whatever views you are wanting to add constraints to, and then you can add constraints for your items that are relative to the views in the stack view. That way, your original views can have leading and trailing constraints within the stack view.
This can be done in the interface builder and programatically.
This question already has good answers,
One suggestion though, use spacing property to set the spacing between the views. For first and last views there can be two options, either set insets as #tolpp suggested or add constraint attaching to parent (stackview) with constant to add padding.
What we did was add transparent components (e.g., UIButton/UIView) as the first and last children of the UIStackView. Then set constrain the width of these invisible children to adjust the padding.
It seems that the solution was pretty simple. Instead of:
stackView.leadingAnchor.constraintEqualToAnchor(selectedContactsScrollView.leadingAnchor).active = true
I just wrote:
stackView.leadingAnchor.constraintEqualToAnchor(selectedContactsScrollView.leadingAnchor, constant: 15).active = true
Just add an empty view at the beginning of the stack view (also constraining its width and/or height):
stackView.insertArrangedSubview(UIView().constrain(width: 0, height: 0), at: 0)
and/or at the end:
stackView.addArrangedSubview(UIView().constrain(width: 0, height: 0))
I created this simple UIView extension to add the constraints:
extension UIView {
func constrain(width: CGFloat, height: CGFloat) -> Self {
NSLayoutConstraint.activate([
widthAnchor.constraint(equalToConstant: width),
heightAnchor.constraint(equalToConstant: height)
])
return self
}
}

Resources