UIView in UITableView is changing frame automatically - ios

I have this UIView added to my UITableView:
self.tableView.addSubview(viewInfoPlus)
self.viewInfoPlus.frame = CGRect.init(x: kWidth - 193 - 32, y: 751, width: 193, height: 103)
The one signaled with the red arrow.
The UIView in the screenshot have the right frame but when I try to reload the UITableView or just some cell/cells using:
let indexPath = IndexPath.init(row: 0, section: TableSection.plus.rawValue)
UIView.setAnimationsEnabled(false)
self.tableView.beginUpdates()
tableView.reloadRows(at: [indexPath], with: .none)
self.tableView.endUpdates()
UIView.setAnimationsEnabled(true)
The UIView automatically change the frame to the wrong one as you can see in this screenshot.
But when the user scroll to the top of UITableVIew the UIView automatically change again their frame to the right one.
Obviously I never changed the frame in the code.

Use Constraints instead of frame.
Setting Frames there is no guarantee when resize. Autolayout allows view that dynamically adjust to different size classes and positions. Check following example
Example:
viewInfoPlus.translatesAutoresizingMaskIntoConstraints = false
viewInfoPlus.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
viewInfoPlus.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 20).isActive = true
viewInfoPlus.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewInfoPlus.heightAnchor.constraint(equalToConstant: 103).isActive = true

Related

Adding a separator line under custom tableview cell in UITableViewCell

I am trying to add a separator to my uitableview cell. i tried this.
let separator = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 8.0))
cell.contentView.addSubview(separator)
But this adds the separator view on top of the cell, i need it to the bottom.
I also tried this way.
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
cell.contentView.addSubview(view)
view.leadingAnchor.constraint(equalTo: cell.contentView.leadingAnchor).isActive = true
view.trailingAnchor.constraint(equalTo: cell.contentView.trailingAnchor).isActive = true
view.heightAnchor.constraint(equalToConstant: 8.0).isActive = true
view.topAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
But this gives me no common ancestor error. and i don't want to use storyboard. i need it because i am using same cell at different places, somewhere i need the separator somewhere not. what should i do?
Change constraint for this
view.topAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
to
view.bottomAnchor.constraint(equalTo: cell.contentView.bottomAnchor).isActive = true
its better to create the line inside init of the custom cell and make it a property
let view = UIView()
then mange its state from cellForRowAt
view.isHidden = true/false
Here is a better way to use separators:
First enable separators in your UITableView by:
myTableView.separatorStyle = .singleLine
Then at your cellForRowAt function:
// Create your cell
// if you want to show the separator then
cell.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
// if you want to hide the separator then
self.separatorInset = UIEdgeInsets(top: 0, left: UIScreen.main.bounds.width, bottom: 0, right: 0)
This would work for cells in the same UITableView as well. Because by adding a left inset of screen width then it won't show on screen and if you set it to 0 it'll be displyed from left edge to right edge of the screen.
Also you can change the color or the insets of the separator by using other properties without using storyboards or xibs.
you can just try below line under viewDidLoad() that include the tableview
override func viewDidLoad() {
tableView.separatorStyle = .singleLine
}

Animate UITableViewCell height

I have a container view inside a UITableViewCell which is attached to the 4 edges of the contentview with a padding. I can resize the containerView by pinching changing the height and padding. All the constraints update correctly but the contentView height doesn't change. Here are the constraints of the containerView.
contentView.addSubview(containerView)
containerView.translatesAutoresizingMaskIntoConstraints = false
containerViewLeadingAnchor =
containerView.leadingAnchor .constraint(equalTo: contentView.leadingAnchor, constant: 10)
containerViewTopAnchor =
containerView.topAnchor .constraint(equalTo: contentView.topAnchor, constant: 10)
containerViewBottomAnchor =
containerView.bottomAnchor .constraint(equalTo: contentView.bottomAnchor, constant: -10)
containerViewHeightAnchor =
containerView.heightAnchor .constraint(equalToConstant: 146)
containerViewWidthAnchor =
containerView.widthAnchor .constraint(equalToConstant: UIScreen.main.bounds.width - 20)
containerViewLeadingAnchor.isActive = true
containerViewTopAnchor.isActive = true
containerViewBottomAnchor.isActive = true
containerViewHeightAnchor.isActive = true
containerViewWidthAnchor.isActive = true
And here is how i update them inside the UITableViewCell
containerViewLeadingAnchor.constant = containerPaddingForComfortable - (containerPaddingForComfortable * scaleRatio)
containerViewTopAnchor.constant = containerPaddingForComfortable - (containerPaddingForComfortable * scaleRatio)
containerViewWidthAnchor.constant = (UIScreen.main.bounds.width - 20) + (containerPaddingForComfortable*2 * scaleRatio)
containerViewBottomAnchor.constant = -containerPaddingForComfortable + (containerPaddingForComfortable * scaleRatio)
containerView.layer.cornerRadius = containerCornerRadius - (containerCornerRadius * scaleRatio)
self.layoutIfNeeded()
It is not enough to simply modify view frame or constraints to update the size of content view.
In table view the cell content view is controlled by cell which is controlled by table view or rather it's data source or delegate. In your case you need to have cell height set to automatic dimensions so constraints are used internally to determine cell size. Once that is done you will need to call beginUpdates() and endUpdates() on the table view. So something like:
func resizeMyCell(_ cell: MyCell, to height: CGFloat) {
cell.heightConstraint.constant = height
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
Although it might seem strange the two calls will invalidate sizes and refresh table view cell sizes and positions. By the way changing cell size usually means also moving rest of the cells beneath the resized one.

How to use a slide animation rather than a "ghosting" animation when a UICollectionViewCell changes position due to a change in height of it's parent?

I'm building a UICollectionView which has two cells. When the user taps a button, the second cell needs to move from directly below the first cell to directly to the right of the first cell (off screen) and allow the user to scroll over to it.
The kicker is that I need it to appear to slide to the new position. Using my current code, it's just "ghosting".
Here's how I'm doing it now:
Initialize the UICollectionView with UICollectionViewFlowLayout with horizontal scrolling, paging enabled, and a height of two cells plus margin (196pt * 2 + 8pt = 400pt)
func setupTestCollectionView() {
// Create the layout, the frame, and the collection view
let testLayout = UICollectionViewFlowLayout()
let testFrame = CGRect()
let collectionView = UICollectionView(frame: testFrame, collectionViewLayout: testLayout)
// Specify the attributes of the elements we just created and add them to the primary view
testLayout.scrollDirection = UICollectionViewScrollDirection.horizontal
testLayout.minimumLineSpacing = 8
testLayout.minimumInteritemSpacing = 0
collectionView.translatesAutoresizingMaskIntoConstraints = false
collectionView.isPagingEnabled = true
collectionView.clipsToBounds = false
collectionView.backgroundColor = UIColor.green
collectionView.register(ArtworkCollectionViewCell.self, forCellWithReuseIdentifier: "artworkCell")
collectionView.delegate = self
collectionView.dataSource = self
view.addSubview(collectionView)
// Set all of the constraints to position the UICollectionView in the primary view then append them to the initialConstraints array
let topConstraint = collectionView.topAnchor.constraint(equalTo: view.topAnchor, constant: 8)
let leadingConstraint = collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 0)
let trailingConstraint = collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 0)
testHeightConstraint = collectionView.heightAnchor.constraint(equalToConstant: 400)
// testHeightConstraint is declared outside of this function so we can modify it in a different funcction when the user taps a button
initialConstraints.append(contentsOf: [topConstraint, leadingConstraint, trailingConstraint, testHeightConstraint])
}
Change the height constraint of the UICollectionView when the user taps a button to a height of 1 cell (196pt) and animate the layout update
func testButtonAction() {
// Change the height constraint of the UICollectionView
testHeightConstraint.constant = 196
// Animate the layout update
UIView.animate(withDuration: 0.2) {
self.view.layoutIfNeeded()
}
}
This achieves the function I'm looking for (see screenshots below), but so far I've been unable to figure out how to change the animation. Any help with this is greatly appreciated!
Thank you in advance!
Screenshot on View Load:
Screenshot after Button is Tapped:
Screenshot of Scrolling (to show functionality & position):

Make a View stick to bottom with UITableViewController

I want to make a UIView stick on the bottom while I am scrolling in my UITableView.
My idea was to set a UIView with the position of right above the navigation item. Set it's zPosition to 1.
The problem is, that the yPosition of my UITableView varies.
Any idea how to solve this?
Edit:
Providing Screenshots for visible vs. expected behaviour:
Visible:
This is when I scroll:
Expected:
As seen on Tinder Camera Symbol above Table:
Edit2:
This code is what I use to put the rectangle to the bottom.
It works until I swipe the UITableView - The rectangle also scrolls up.
let bounds = self.view.bounds
let yPosition = self.navigationController?.toolbar.frame.minY
print(yPosition)
let myView = UIView(frame: CGRect(x: 0, y: yPosition! - bounds.height/6, width: bounds.width, height: bounds.height/6))
myView.backgroundColor = myColor.rookie
myView.alpha = 0.8
myView.layer.zPosition = 1
myView.isUserInteractionEnabled = true
self.view.addSubview(myView)
there is a solution for this. you can do this by disabling the Auto Layout(button.translatesAutoresizingMaskIntoConstraints = false) property of the corresponding Button or any UIView for floating button:
Swift 4
button.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
button.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor, constant: -10).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
} else {
button.rightAnchor.constraint(equalTo: tableView.layoutMarginsGuide.rightAnchor, constant: 0).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.layoutMarginsGuide.bottomAnchor, constant: -10).isActive = true
}

Constraints not updating with device orientation change

I have a color slider in a tableView cell that extends from the label to the trailingAnchor of the cell. The issue I am having is when the device orientation changes the color slider does not update its constraints and no longer extends the full length of the cell. Below is my code to set up the constraints on the color slider. Below are images to show the issue I am having. If my phone is already in landscape orientation when I present this scene the color slider extends the full length of the cell as desired. However, if I switch to landscape when already viewing this scene the slider appears as below. Here is the full code if that helps.
func configureColorSlider() {
let colorSlider = ColorSlider()
let xCell = colorCell.contentView.bounds.width
let yCell = colorCell.contentView.bounds.height
colorSlider.frame = CGRect(x: xCell / 4, y: yCell / 4, width: 200, height: 24)
colorSlider.orientation = .horizontal
colorSlider.addTarget(self, action: #selector(ConfigureTimestampTableViewController.changedColor(_:)), for: .valueChanged)
colorCell.contentView.addSubview(colorSlider)
colorSlider.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([colorSlider.leadingAnchor.constraint(equalTo: colorLabel.trailingAnchor, constant: 8),
colorSlider.trailingAnchor.constraint(equalTo: colorCell.contentView.trailingAnchor, constant: -8),
colorSlider.topAnchor.constraint(equalTo: colorCell.contentView.topAnchor, constant: 8),
colorSlider.bottomAnchor.constraint(equalTo: colorCell.contentView.bottomAnchor, constant: -8) ])
}
CALayers will not be resized in response to their parent UIView being resized, whether by auto layout or manually.
You just need to add code to your ColorSlider that updates the layer when the size/position of the ColorSilider changes. Luckily, there is a method on UIView that is specifically for this.
override func layoutSubviews() {
super.layoutSubviews()
gradientLayer.frame = bounds
}

Resources