Strange scrolling of UITableView while scrolling and cell height change are executed - ios

I have a UITableView with a lot of cells. On the cells that have a UITextField i scroll the table if the keyboard would hide the textfield. And to some cells i added a change of cell height to the scroll.
This works fine at the beginning and middle of the table but if the cell in question is at the bottom of the table I am getting some strange jumping around and vanishing of cells.
After the user has finished the editing the height of the cell is changed back to normal and the vanished cells reappear but the table is scrolled a bit upwards.
My question is now how can I get rid of this jumping and vanishing of cells?
Here is my code:
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if fieldIndex != [-99, -99] {
if indexPath.section == fieldIndex[0] && indexPath.row == fieldIndex[1] {
fieldIndex = [-99, -99]
return 220.0
}
}
return 80.0
}
func keyboardWillBeShown(_ sender: Notification) {
let info: NSDictionary = (sender as NSNotification).userInfo! as NSDictionary
let value: NSValue = info.value(forKey: UIKeyboardFrameBeginUserInfoKey) as! NSValue
let keyboardSize: CGSize = value.cgRectValue.size
let height = toolBar.frame.height
let contentInset: UIEdgeInsets = UIEdgeInsetsMake(0.0, 0.0, keyboardSize.height + toolBar.frame.height + 60, 0.0)
originalIntersect = tableView.contentInset
tableView.contentInset = contentInset
tableView.scrollIndicatorInsets = contentInset
var aRect: CGRect = self.view.frame
aRect.size.height -= keyboardSize.height
}
func keyboardWillBeHidden(_ sender: Notification) {
let insets: UIEdgeInsets = UIEdgeInsetsMake(self.tableView.contentInset.top, 0, 0, 0)
tableView.contentInset = originalIntersect
tableView.scrollIndicatorInsets = originalIntersect
}
If there is other code you need to see to help just ask.
Regards and Thanks
Adarkas

Related

How to prevent the last table cell from overlapping with the keyboard

I have a chat app that displays the messages in a table view. When I invoke the keyboard, I want:
The table view to scroll to the bottom
I want there to be no overlap between any messages and the keyboard.
#objc private func keyboardWillShow(notification: NSNotification) {
if let userInfo = notification.userInfo {
let keyBoardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue
let keyboardViewEndFrame = view.convert(keyBoardFrame!, from: view.window)
let keyboardHeight = keyboardViewEndFrame.height
tableView.scrollToBottom() { indexPath in
let rectofCell = self.tableView.rectForRow(at: indexPath)
let lastCellFrame = self.tableView.convert(rectofCell, from: self.view.window)
if lastCellFrame.origin.y + lastCellFrame.size.height > keyboardViewEndFrame.origin.y {
let overlap = lastCellFrame.origin.y + lastCellFrame.size.height - keyboardViewEndFrame.origin.y
self.tableView.frame.origin.y = -overlap
}
}
}
}
extension UITableView {
func scrollToBottom(animated: Bool = true, completion: ((IndexPath) -> Void)? = nil) {
let sections = self.numberOfSections
let rows = self.numberOfRows(inSection: sections - 1)
if (rows > 0){
let indexPath = IndexPath(row: rows - 1, section: sections - 1)
self.scrollToRow(at: indexPath, at: .bottom, animated: true)
completion?(indexPath)
}
}
}
The value for rectOfCell shows (0.0, 5305.333518981934, 375.0, 67.33333587646484) and the converted value (0.0, 9920.000185648601, 375.0, 67.33333587646484) and the table view disappears out of the screen.
I only want to move the table view upward if the last message overlaps with the eventual position of the keyboard. For example, when the keyboard is invoked (either when the table view is already at the bottom or not at the bottom), the table view shouldn't move upward if the appearance of the keyboard doesn't cover the messages (i.e., there is only one message).
You don't need to manage tableView.frame for such a thing. You just need to make sure that when keyboard appears, tableView adds necessary contentInset value from bottom so that user can still see all the content inside tableView with the keyboard still on screen.
All you need is this -
// when keyboard appears
let insets = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets
// when keyboard disappears
let insets: UIEdgeInsets = .zero
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets

Autoresize UICollectionView cells, Align cell to top

I've a UICollectionView, with multiple sections and rows.
Header and Footer views wherever necessary, of fixed size.
Cells that are autoresizable
The cell view are designed like :
Green colour - ImageView
Orange colour - Labels with numberOfLines = 0.
The cell should expand it's size according to label numberOfLines.
I've achieved this using this code in MyCustomCell :
override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
super.apply(layoutAttributes)
let autoLayoutAttributes = super.preferredLayoutAttributesFitting(layoutAttributes)
let targetSize = CGSize(width: Constants.screenWidth/3.5, height: 0)
let autoLayoutSize = contentView.systemLayoutSizeFitting(targetSize, withHorizontalFittingPriority: UILayoutPriority.required, verticalFittingPriority: UILayoutPriority.defaultLow)
let autoLayoutFrame = CGRect(origin: autoLayoutAttributes.frame.origin, size: autoLayoutSize)
autoLayoutAttributes.frame = autoLayoutFrame
return autoLayoutAttributes
}
The cells are autoresizing but the contentView (in cyan colour) are Centre aligned both vertically and horizontally.
I need to make it vertically align to Top.
I had the alignment problem with headers and footers too. For that i've subclassed UICollectionViewFlowLayout
class MainCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func invalidationContext(forPreferredLayoutAttributes preferredAttributes: UICollectionViewLayoutAttributes, withOriginalAttributes originalAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutInvalidationContext {
let context: UICollectionViewLayoutInvalidationContext = super.invalidationContext(forPreferredLayoutAttributes: preferredAttributes, withOriginalAttributes: originalAttributes)
let indexPath = preferredAttributes.indexPath
context.invalidateSupplementaryElements(ofKind: UICollectionView.elementKindSectionFooter, at: [indexPath])
context.invalidateSupplementaryElements(ofKind: UICollectionView.elementKindSectionHeader, at: [indexPath])
return context
}
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
var topMargin = sectionInset.top
var leftMargin = sectionInset.left
var maxY: CGFloat = -1.0
attributes?.forEach { layoutAttribute in
guard layoutAttribute.representedElementCategory == .cell else {
return
}
if layoutAttribute.frame.origin.y >= maxY {
leftMargin = sectionInset.left
topMargin = sectionInset.top
}
layoutAttribute.frame.origin.x = leftMargin
leftMargin += layoutAttribute.frame.width + minimumInteritemSpacing
maxY = max(layoutAttribute.frame.maxY , maxY)
}
return attributes
}
}
Here is an image to illustrate current situation. The cyan are contentView of cells. I've to make it align to Top.
EDIT:
So i realised that UICollectionViewFlowLayout code was creating more bugs than fixing a problem. I added layoutAttributesForElements to left align my cell in case there is only one cell. What it actually did was align all of my cells to the left.
Modified the code as
class MainCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attributes = super.layoutAttributesForElements(in: rect)
if attributes?.count == 1 {
if let currentAttribute = attributes?.first {
currentAttribute.frame = CGRect(x: self.sectionInset.left, y: currentAttribute.frame.origin.y, width: currentAttribute.frame.size.width, height: currentAttribute.frame.size.height)
}
}
return attributes
}
}
Now my UICollectionViewCell are properly aligned to horizontal centre with exception to if only one cell which will be left aligned.
Still no solution for vertical alignment.
So I worked around this for quite a bit. No answers from stack overflow help in any way. So i played with custom UICollectionViewFlowLayout method layoutAttributesForElements.
This method will call for every section, and will have layoutAttributesForElements for rect. This will give an array of UICollectionViewLayoutAttributes which one can modify as per the need.
I was having problem figuring out the y coordinate which I would like to change for my cells.
So first with a loop in attributes from layoutAttributesForElements I got the header and saved it's height in a variable. Then changed the rest of cell's y coordinate with the headerHeight value.
Not sure if this is the right way or not, nor did any performance testing. For now everything seems fine without any lag or jerk.
Here is full code or custom UICollectionViewFlowLayout.
class MainCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
let attr = super.layoutAttributesForElements(in: rect)
var attributes = [UICollectionViewLayoutAttributes]()
for itemAttributes in attr! {
let itemAttributesCopy = itemAttributes.copy() as! UICollectionViewLayoutAttributes
// manipulate itemAttributesCopy
attributes.append(itemAttributesCopy)
}
if attributes.count == 1 {
if let currentAttribute = attributes.first {
currentAttribute.frame = CGRect(x: self.sectionInset.left, y: currentAttribute.frame.origin.y, width: currentAttribute.frame.size.width, height: currentAttribute.frame.size.height)
}
} else {
var sectionHeight: CGFloat = 0
attributes.forEach { layoutAttribute in
guard layoutAttribute.representedElementCategory == .supplementaryView else {
return
}
if layoutAttribute.representedElementKind == UICollectionView.elementKindSectionHeader {
sectionHeight = layoutAttribute.frame.size.height
}
}
attributes.forEach { layoutAttribute in
guard layoutAttribute.representedElementCategory == .cell else {
return
}
if layoutAttribute.frame.origin.x == 0 {
sectionHeight = max(layoutAttribute.frame.minY, sectionHeight)
}
layoutAttribute.frame = CGRect(origin: CGPoint(x: layoutAttribute.frame.origin.x, y: sectionHeight), size: layoutAttribute.frame.size)
}
}
return attributes
}
}
NOTE: You need to copy the attributes in an array first to work with. Else xcode will yell Logging only once for UICollectionViewFlowLayout cache mismatched frame in console.
If any new/perfect solution is there please let me know.
CHEERS!!!

Set width and height of UITextView based on the dynamic text

I'm building a chat application where each message are inserted into a row inside a table. Each row contains an avatar and a message. I want to set the width and height of the UITextArea as per the length of the text to put inside.
Below is the code I've used. But here, both height and width are constant (200x50)
PS: I'm a newbie to Swift and ios and I'm using Swift 3. Every code I got after searching is in objective-c or swift 2
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = Bundle.main.loadNibNamed("ChatBox1", owner: self, options: nil)?.first as! ChatBox1
let myTextField: UITextView = UITextView(frame: CGRect(x: 30, y: 20, width: 200.00, height: 50.00));
cell.addSubview(myTextField)
myTextField.isScrollEnabled = false
myTextField.isUserInteractionEnabled = false
myTextField.backgroundColor = UIColor.lightGray
myTextField.text = "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
myTextField.layer.cornerRadius = 5
print(myTextField.intrinsicContentSize)
let image = UIImage(named: "agent")
let imageView = UIImageView(image: image)
imageView.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
cell.addSubview(imageView)
return cell
}
This two methods are done to set Height and width of textview according to the text on chat.
You can use these. (swift 2.3)
var screenrect = UIScreen.mainScreen().bounds
func GET_WIDTH(textView : UITextView , text : String)-> CGFloat
{
textView.text = text;
let size = textView.bounds.size
let newSizeWidth = textView.sizeThatFits(CGSize(width: CGFloat.max, height: size.height))
// Resize the cell only when cell's size is changed
if size.width != newSizeWidth.width
{
if( newSizeWidth.width > screenrect.width-120 )
{
textView.layer.frame.size.width = screenrect.width-120;
}
else if ( newSizeWidth.width < 40 )
{
textView.layer.frame.size.width = 40
}
else
{
textView.layer.frame.size.width = newSizeWidth.width;
}
}
return textView.layer.frame.size.width + 40;
}
func GET_HEIGHT(textView : UITextView , text : String)-> CGFloat
{
textView.text = text;
let size2 = textView.bounds.size
let newSize = textView.sizeThatFits(CGSize(width: size2.width, height: CGFloat.max))
if size2.height != newSize.height
{
if( newSize.height < 40 )
{
textView.layer.frame.size.height = 40
}
else
{
textView.layer.frame.size.height = newSize.height
}
}
return textView.layer.frame.size.height + 15;
}
You have to call the functions of the textview like this to set height and width.
let textViewRight=UITextView(frame: CGRectMake(0, 4, 40, 40));
textViewRight.layer.frame.size.width = GET_WIDTH(textViewRight, text: currentObject.chat_userMessage)
textViewRight.layer.frame.size.height = GET_HEIGHT(textViewRight, text: currentObject.chat_userMessage)
Try this code:
Answer 1:
func tableView(tableView:UITableView!, numberOfRowsInSection section: Int) -> Int {
yourTableViewName.estimatedRowHeight = 44.0 // Standard tableViewCell size
yourTableViewName.rowHeight = UITableViewAutomaticDimension
return yourArrayName.count }
And also put this code inside your Cell for incase...
yourCell.sizeToFit()
Answer 2:
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
override func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableViewAutomaticDimension
}
in viewDidLoad Add this
tableView.estimatedRowHeight = 44.0
tableView.rowHeight = UITableViewAutomaticDimension
Just add these two lines and your problem will b solved
Take textView height constraint and set it equal to the content size of the textView.
Alternatively, if you use autolayout you can set the content hugging and compression property to 1000. It should expand your cell according to the content.

Oblique table view cells in iOS

How can I implement this? A table view with oblique cells. I was thinking first to make the cells overlap and cut out a piece, but I can't make it work.
Now the single solution I can think of is to download the second image in first cell and cut out the top triangle and add to the first cell. But that wouldn't be too optimal memory wise and processing wise if the user is scrolling through the list.
I appreciate any advice, thank you!
I've done something similar like this but I didn't use UITableView or UICollectionView.
What I use is having one UIImageView(AView) on top of the other UIImageView(BView) in view hieararchy. So I add UISwipeGestureRecognizer to BView .And at the top of BView I've added a seperator view(when VC load user could not see it). And I basically animating BView however I want.
But since they are not cells in UITableView you can't give "pulling" behavior to user which is a UX disadvantage for your app.
I solved it the following way. I used UICollectionView in the end because I couldn't find a way to overlap nicely the cells from UITableView. So first I made a custom layout which looks like this:
CustomCollectionViewFlowLayout.swift
import UIKit
class CustomCollectionViewFlowLayout: UICollectionViewFlowLayout {
override func collectionViewContentSize() -> CGSize {
let xSize = self.itemSize.width
let ySize = CGFloat(self.collectionView!.numberOfItemsInSection(0)) * self.itemSize.height
var size = CGSizeMake(xSize, ySize)
if self.collectionView!.bounds.size.width > size.width {
size.width = self.collectionView!.bounds.size.width
}
if self.collectionView!.bounds.size.height > size.height {
size.height = self.collectionView!.bounds.size.height
}
return size
}
override func layoutAttributesForElementsInRect(rect: CGRect) -> [AnyObject]? {
let attributesArray = super.layoutAttributesForElementsInRect(rect) as! [UICollectionViewLayoutAttributes]
let numberOfItems = self.collectionView?.numberOfItemsInSection(0)
for attribute in attributesArray {
let xPosition = attribute.center.x
var yPosition = attribute.center.y
if attribute.indexPath.row == 0 {
attribute.zIndex = Int(INT_MAX)
} else {
yPosition -= CGFloat(60 * attribute.indexPath.row)
attribute.zIndex = numberOfItems! - attribute.indexPath.row
}
attribute.center = CGPointMake(xPosition, yPosition)
}
return attributesArray
}
override func layoutAttributesForItemAtIndexPath(indexPath: NSIndexPath) -> UICollectionViewLayoutAttributes! {
return UICollectionViewLayoutAttributes(forCellWithIndexPath: indexPath)
}
}
Then I used paths and shapes to cut out that part and also draw a white rectangle. So it looks like this:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("ChallengeCollectionViewCell", forIndexPath: indexPath) as! ChallengeCollectionViewCell
cell.challengeImage.sd_setImageWithURL(NSURL(string: STATIC_IMAGE_URL), completed: nil)
let trianglePath = CGPathCreateMutable()
CGPathMoveToPoint(trianglePath, nil, 0, 0)
CGPathAddLineToPoint(trianglePath, nil, 0, cell.challengeImage!.frame.size.height)
CGPathAddLineToPoint(trianglePath, nil, cell.challengeImage!.frame.size.width, cell.challengeImage!.frame.size.height - 50)
CGPathAddLineToPoint(trianglePath, nil, cell.challengeImage!.frame.width, 0)
CGPathAddLineToPoint(trianglePath, nil, 0, 0)
CGPathCloseSubpath(trianglePath)
let shapeLayer = CAShapeLayer()
shapeLayer.frame = cell.challengeImage!.frame
shapeLayer.path = trianglePath
cell.challengeImage.layer.mask = shapeLayer
cell.challengeImage.layer.masksToBounds = true
let linePath = CGPathCreateMutable()
CGPathMoveToPoint(linePath, nil, 0, cell.challengeImage!.frame.size.height)
CGPathAddLineToPoint(linePath, nil, cell.challengeImage!.frame.size.width, cell.challengeImage!.frame.size.height - 50)
CGPathCloseSubpath(linePath)
let lineShape = CAShapeLayer()
lineShape.path = linePath
lineShape.lineWidth = 10
lineShape.strokeColor = UIColor.whiteColor().CGColor
cell.challengeImage.layer.addSublayer(lineShape)
return cell
}
I hope it will be useful for someone who runs into the same problem. Thanks and best wishes!

How to populate UITableView from the bottom upwards?

is it possible to reverse the order of a tableView. I have searched a lot for a solution but all the results have not quite been a solution to what I am trying to achieve. They all suggest scrolling to the last position of a table with scrollToRowAtIndexPath and populating the data in reverse. But this doesn't work if the table content is dynamic and in some instances not all the cells have data. For example in a normal tableView the order is:
label 1
label 2
label 3
empty
empty
scroll direction
v
V
the desired result would be:
scroll direction
^
^
empty
empty
empty
label 3
label 2
label 1
in this example if I used the suggested method of scrollToRowAtIndexPath and use the length of the array of objects, I would only get the third cell from the top. And end up with something like this:
unwanted outcome:
label 3
label 2
label 1
empty
empty
scroll direction
v
V
any help would be great thank you.
To populate UITableView from the bottom:
- (void)updateTableContentInset {
NSInteger numRows = [self.tableView numberOfRowsInSection:0];
CGFloat contentInsetTop = self.tableView.bounds.size.height;
for (NSInteger i = 0; i < numRows; i++) {
contentInsetTop -= [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
if (contentInsetTop <= 0) {
contentInsetTop = 0;
break;
}
}
self.tableView.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0);
}
To reverse the order of elements:
dataSourceArray = dataSourceArray.reverseObjectEnumerator.allObjects;
Swift 4.2/5 version:
func updateTableContentInset() {
let numRows = self.tableView.numberOfRows(inSection: 0)
var contentInsetTop = self.tableView.bounds.size.height
for i in 0..<numRows {
let rowRect = self.tableView.rectForRow(at: IndexPath(item: i, section: 0))
contentInsetTop -= rowRect.size.height
if contentInsetTop <= 0 {
contentInsetTop = 0
break
}
}
self.tableView.contentInset = UIEdgeInsets(top: contentInsetTop,left: 0,bottom: 0,right: 0)
}
Swift 3/4.0 version:
self.tableView.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0)
first reverse uitableview
tableView.transform = CGAffineTransformMakeScale (1,-1);
then reverse cell in cell create.
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
...
cell.contentView.transform = CGAffineTransformMakeScale (1,-1);
Swift 4.0 and 4.2 version
First reverse UITableView in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
tableView.transform = CGAffineTransform(scaleX: 1, y: -1)
}
Then reverse the cell in cellForRowAt.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableViewCell", for: indexPath) as? MyTableViewCell else { fatalError() }
cell.contentView.transform = CGAffineTransform(scaleX: 1, y: -1)
return cell
}
Here is a refined solution of KlimczakM´s solution that works with autolayouted tableview cells (as well as the fixed ones). This solution also works with sections, section headers and section footers.
Swift 3.0:
func updateTableContentInset(forTableView tv: UITableView) {
let numSections = tv.numberOfSections
var contentInsetTop = tv.bounds.size.height -
(self.navigationBar?.frame.size.height ?? 0)
for section in 0..<numSections {
let numRows = tv.numberOfRows(inSection: section)
let sectionHeaderHeight = tv.rectForHeader(inSection: section).size.height
let sectionFooterHeight = tv.rectForFooter(inSection: section).size.height
contentInsetTop -= sectionHeaderHeight + sectionFooterHeight
for i in 0..<numRows {
let rowHeight = tv.rectForRow(at: IndexPath(item: i, section: section)).size.height
contentInsetTop -= rowHeight
if contentInsetTop <= 0 {
contentInsetTop = 0
break
}
}
// Break outer loop as well if contentInsetTop == 0
if contentInsetTop == 0 {
break
}
}
tv.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0)
}
NOTE:
Above code is untested but should work.
Just make sure that you cope for the height of any navbar or tabbar and you'll be fine. In the code above i only do that for the navbar!
Don't bother to write the code by yourself.
Why don't you use ReverseExtension. Its very easy and will give you all required results. Please follow this url
https://github.com/marty-suzuki/ReverseExtension
Note: Whenever you need to add a new cell, please insert newly added model at zeroth index of datasource array, so new cell should add at bottom. Otherwise it would add the cell at top and you would get confused again.
The simple way is use like UILabel multiple lines + autolayout.
-> UITableView should resize it base on it content(aka intrinsic layout).
Create your tableview and set base class following:
class IntrinsicTableView: UITableView {
override var contentSize:CGSize {
didSet {
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return CGSize(width: UIViewNoIntrinsicMetric, height: contentSize.height)
}
}
Now set left right and bottom layout constraints for your tableview pin to it parent view.
The most important is top layout constraint should set to great than or equal 0, This condition guaranteed table will not tall than it parent view.
I did in cellForRowAt method:
let reverseIndex = myArray.count-indexPath.row-1
let currCellData = myArray.object(at: reverseIndex)
and then you continue working with currCellData
//Add these lines where you want to reload your tableView
let indexpath = IndexPath(row: self.Array.count-1, section: 0)
self.tableView.scrollToRow(at: indexpath, at: .top, animated: true)
self.updateTableContentInset()
//Add this function below
func updateTableContentInset() {
let numRows = self.tableView.numberOfRows(inSection: 0)
var contentInsetTop = self.tableView.bounds.size.height
for i in 0..<numRows {
let rowRect = self.tableView.rectForRow(at: IndexPath(item: i, section: 0))
contentInsetTop -= rowRect.size.height
if contentInsetTop <= 0 {
contentInsetTop = 0
break
}
}
self.tableView.contentInset = UIEdgeInsets(top: contentInsetTop,left: 0,bottom: 0,right: 0)
}
I you want that every new cell should appear from the bottom of the tableView, use this:-
Invert the tableView:
myTableView.transform = CGAffineTransform (scaleX: -1,y: -1)
Invert the cells also:
cell.contentView.transform = CGAffineTransform (scaleX: -1,y: -1)
Now populate your tabelViewDataSource in opposite direction, like if you are using an array, then you may do like this:
myTableViewData.insert(<Your New Array Element>), at: 0)
This solution adjust the content inset as the content size changes using KVO. It also takes the content inset into account when scrolling to top as simply scrolling to CGPointZero will scroll to the top of the content instead of scrolling to the top of the table.
-(void)startObservingContentSizeChanges
{
[self.tableView addObserver:self forKeyPath:kKeyPathContentSize options:0 context:nil];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if([keyPath isEqualToString:kKeyPathContentSize] && object == self.tableView)
{
// difference between content and table heights. +1 accounts for last row separator
CGFloat height = MAX(self.tableView.frame.size.height - self.tableView.contentSize.height, 0) + 1;
self.tableView.contentInset = UIEdgeInsetsMake(height, 0, 0, 0);
// "scroll" to top taking inset into account
[self.tableView setContentOffset:CGPointMake(0, -height) animated:NO];
}
}
Swift 3.01 - Other solution can be, rotate and flip the tableView.
self.tableView.transform = CGAffineTransform.init(rotationAngle: (-(CGFloat)(M_PI)))
self.tableView.transform = CGAffineTransform.init(translationX: -view.frame.width, y: view.frame.height)
UITableView anchor rows to bottom
If you have an array of object you display in cellForRowAtIndexPath:, lets say dataArray you can reverse it, or in cellForRowAtIndexPath:
you can do something like that:
NSString *yourObject = dataArray[[dataArray count] - 1 - indexPath.row];
cell.textLabel.text = yourObject
I assume you keep strings in dataArray.

Resources