I have a UITextView inside a custom TableView cell. I want the text view to scroll to the top by default. I usually do it this way in regular views:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
let fixedWidth = bioView.frame.size.width
bioView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
let newSize = bioView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
var newFrame = bioView.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
bioView.frame = newFrame;
bioView.scrollRangeToVisible(NSRange(location:0, length:0))
}
bioView is my text view object. However, this doesn't work in my case since I can't reference the textview in the ViewController class and can only do so in the CustomCell class and that class cannot override viewDidLayoutSubviews. Any help?
Try this.
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
CustomCell* cell = [tableView dequeueReusableCellWithIdentifier:#"CustomCell"];
[cell.textView scrollRangeToVisible:NSMakeRange(0, 0)];
return cell;
}
Swift 3.0:
let cell = tableView.dequeueReusableCell(withIdentifier: "CustomCell", for: indexPath) as! DemoCell
cell.textView.scrollRangeToVisible(NSRange(location:0, length:0))
It works! However, the table has to scroll for the changes to take effect, best way I found so far. Thanks everyone!
In your viewController which handles UITableViewDelegate events, override willDisplayCell method. Identify your textView from Cell and write the code to scroll.
I have tried doing the scrolling, either with scrollRangeToVisible() or setting contentOffset to .zero in awakeFromNib, willDisplayCell, and cellForRowAtIndexPath. These all get executed, but the scrolling is still reset (by AutoLayout?) some time subsequent to this.
For a cell that is on screen when the UITableView appears, performing the scroll in willDisplayCell does not "stick." It does work if the cell is scrolled away and scrolls back on screen.
For a cell that is already on screen that has a UITextView with "too much" text so that it scrolls out the bottom of the TextView, the only way I have found to make the text scroll to the top is to set either scrollRangeToVisible() or contentOffset in the viewDidAppear() of the UITableViewController. Even viewWillAppear() did not work.
One drawback is that this solution causes a jump in the scroll location after the TableView appears on screen, even in a performWithoutAnimation block.
You can put the code in viewDidLayoutSubviews, and it will work, but as mentioned this gets called several times, and the text will scroll back to the top with every orientation change, which may not be desirable.
Related
I am trying to implement a scrollView that contains a text field, button, and table view. I am completely lost trying to get the thing to scroll, I've tried changing the height of UITableView but that has not worked, I've tried adjusting the height of the content View and scrollView, but that has not worked either. How can I get the scrollview to scroll and adjust the size when another tableViewCell is added to the tableView?
override func viewDidLayoutSubviews() {
tableView.frame = CGRect(x: tableView.frame.origin.x, y: tableView.frame.origin.y, width: tableView.frame.size.width, height: CGFloat(tableView.numberOfRows(inSection: 0) * 120))
tableView.reloadData()
}
#IBAction func addExercisePressed(_ sender: Any) {
test.append("nice")
tableView.reloadData()
tableView.frame = CGRect(x: tableView.frame.origin.x,y: tableView.frame.origin.y ,width: self.view.frame.size.width, height: CGFloat(test.count * 120))
scrollView.frame.size = CGSize(width: self.view.frame.size.width, height: tableView.frame.size.height)
}
Please dont use tableView inside a scrollView, tableView is subclass of scrollView and that's mean tableView already have scroll function
your answer to set
tableHeight.constant = tableView.contentSize.height
It will make recycle/dequeue cell function useless because tableView will show all the cell immediately, tableview will call cellForRowAt for all indexes that showed in tableview viewport
UITableview inside Scrollview call cellforrow too many times
Here why we use dequeueReusableCellWithIdentifier
From what I see you want something like this right
you can check this repo how I only use a tableview to achieved that
please note that tableHeaderView is scrollable, sectionHeaderView is sticky
All I needed was to set a height constraint for the TableView and connect that via IBOutlet, then this line of code wherever reloading table data is needed
tableHeight.constant = tableView.contentSize.height
I am creating a dynamic height TableViewCell based on the contents of the Cell. I would like to create a custom separator between the cells. I have the following code in the TableViewCell swift file. It appears that the custom separator is created using the default height of the cell and not the calculated height of the cell based on the contents of the cell - which was calculated in the TableView heightForRowAt override function. As a result, the customer separator is rendered across the cell. It appears that the awakeFromNib is called before the height calculation. Any suggestions?
class FeedTableViewCell: UITableViewCell {
override func awakeFromNib() {
super.awakeFromNib()
// add a custom seperator between table view cells.
let screenSize = UIScreen.main.bounds
let separatorHeight = CGFloat(10.0)
let additionalSeparator = UIView.init(frame: CGRect(x: 0, y: self.frame.size.height-separatorHeight, width: screenSize.width, height: separatorHeight))
additionalSeparator.backgroundColor = UIColor(named: "TEXTVIEWCELL_SEPARATOR")
self.addSubview(additionalSeparator)
}
}
The awakeFromNib is called before the height calculation. While the solution works for fixed height TableViewCells, it won't work for variable height TableViewCells. The solution that I used was to create a view on the bottom of the TableViewCell, use constraints to pin it to the bottom of the TableViewCell. You can size the view to 10pt and of course set the color. I did this in StoryBoards, not programmatically.
I have a UITableView that has few cells with different heights. Each row's is dynamically adjusted according to the content it contains.
I have a custom UITableViewCell with a UICollectionView in it that should be resized according to how many cells it has (all the cells should be visible, without inside scrolling).
I also have a UITableViewCell with another UITableView that should be resized according to how many cells it has (all the cells should be visible, without inside scrolling).
The problem is that I don't have the contentSize of the collectionView and the tableView while heightForRow:atIndexPath gets called so I can't set the values to something right.
I've tried to set it to UITableViewAutomaticDimension and to set the cell.contentView.frame.size.height to the contentSize when it got set (added an observer on "contentSize") but then the cells were on top of each other (the collectionView was ontop of the tableView instead of above it).
The tableView code is a regular tableView code.
The collectionView code is:
self.collectionView.collectionViewLayout = UICollectionViewLeftAlignedLayout()
let collectionViewFlowLayout = collectionView.collectionViewLayout as! UICollectionViewFlowLayout
collectionViewFlowLayout.estimatedItemSize = CGSize(width: 34, height: 50)
What is the best way to adjust the size of the tableView and the collectionView?
Thank you!
You can just ask UITableView to adjust its cell height again with this piece of code:
self.tableView.beginUpdates()
self.tableView.endUpdates()
I have a UITextView inside a static UITableViewCell constrained like this:
Picture
In the viewDidLoad() method of my table view class I want to be able to change the text of the UITextView, have the UITextView change size to fit the text (I have scrolling disabled on the UITextView), and then have the UITableViewCell still be constrained as I have intended it to be. This is my attempt to do so:
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.aboutTextView.text = "Some Long String"
//implement self sizing cells
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 400.0
//set the frame of the UITextView to match the size of the text
let fixedWidth = aboutTextView.frame.size.width
let newSize = aboutTextView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.max))
var newFrame = aboutTextView.frame
newFrame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
aboutTextView.frame = newFrame;
//This is returning the new size correctly
//Reload the tableview, nothing happens, text view remains the same size as in the storyboard
self.tableView.reloadData()
}
Although the new frame is bigger than the default size in the storyboard, nothing happens when the tableView is reloaded. I have tried setNeedsLayout() on the UITextView with no luck as well. I also tried constraining the height of the UITextView and changing the constant of the height through an IBOutlet but then the constraints break for obvious reasons. Anybody know why my code isn't working? Any better method to do what I am trying to do?
All the code about the row height should go in your tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) function. That way, whenever the table is reloaded, the code will be executed again, changing the table height.
Edit: I completely misunderstood what you were doing. In the storyboard, try adding constraints to the UITextView within the cell and give it a constraint on the height, and then make sure that the cell also has a height constraint slightly larger than that of the UITextView.
iOS 8 introduced a way for tableViews to automatically adjust their cell's height based on their content (via AutoLayout).
// in viewDidLoad:
tableView.rowHeight = UITableViewAutomaticDimension
tableView.estimatedRowHeight = 44.0
I already got it working with labels, images, etc.
However, I can not figure out a way for the table view cell to grow automatically as soon as a cell's text view changes?
Some more information on the setup:
The UITextView inside a cell has horizontal and vertical constraints to the table view cell's contentView. I also disabled scrolling for the text view.
I have also tried to change the height constraint of the cell manually, but the cell would not adopt those changes as well.
If everything is set up properly (Auto Layout constraints) you do not have to calculate anything yourself.
All you have to do is disable UITextView's scrolling enabled property then on textViewDidChange call tableView.beginUpdates() and tableView.endUpdates().
For a detailed explanation, check out a post I wrote which also includes a working sample project.
Reload the cell in the textViewDidBeginEditing: method of your UITextViewDelegate
[self.tableView beginUpdates];
[self.tableView reloadRowsAtIndexPaths:#[[NSIndexPath indexPathForItem:0 inSection:0]] withRowAnimation:UITableViewRowAnimationNone];
[self.tableView endUpdates];
Obviously you should change the indexpath to the correct one for your cell. This will get the cell to update its constraints and resize itself.
The tableView has to be informed that the textView has changed. Other posts usually answer a "static" textView problem. However, if you are typing inside a table, the cell needs to grow as the textView grows as you type. The textView grows by disabling scrolling. The cell grows by setting the textView's top and bottom constraints to those of the contentView of the cell. This will work once, once the table loads. However to tell the tableView to grow in real time, you have to do in the textView's didChange delegate call
func textViewChanged(onCell cell: YourCustomCell) {
UIView.setAnimationsEnabled(false)
tableView.beginUpdates()
tableView.endUpdates()
UIView.setAnimationsEnabled(true)
}
This will work as you expect. The cell will grow as you type and there won't be a "bouncing".
Solution for Swift 4
func textViewDidChange(_ textView: UITextView) {
let fixedWidth = textView.frame.size.width
let oldSize = textView.frame.size
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat(MAXFLOAT)))
if oldSize.height != newSize.height {
UIView.setAnimationsEnabled(false)
var newFrame = textView.frame
newFrame.size = CGSize(width: fmax(newSize.width, fixedWidth), height: newSize.height)
textView.frame = newFrame
self.tableView.beginUpdates()
self.tableView.endUpdates()
UIView.setAnimationsEnabled(true)
}
}
This is the function I use, which solves the problem of the table view bouncing back after every keystroke.
- (void)textViewDidChange:(UITextView *)textView {
CGFloat fixedWidth = textView.frame.size.width;
CGSize oldSize = textView.frame.size;
CGSize newSize = [textView sizeThatFits:CGSizeMake(fixedWidth, MAXFLOAT)];
// Resize cell only when cell's size changes, to prevent bouncing back and forth.
if (oldSize.height != newSize.height) {
[UIView setAnimationsEnabled:NO];
CGRect newFrame = textView.frame;
newFrame.size = CGSizeMake(fmaxf(newSize.width, fixedWidth), newSize.height);
textView.frame = newFrame;
[self.tableView beginUpdates];
[self.tableView endUpdates];
[UIView setAnimationsEnabled:YES];
}
}
You should disable scroll from your UITextView