UITableViewCell
中有一到三个UITableViewView
。有没有办法在reloadData
之后始终将单元格放在屏幕底部?
+----------------+ +----------------+ +----------------+
| | | | | |
| | | | | |
| | | | | |
| | | | | +------------+ |
| | | | | | cell 1 | |
| | | | | +------------+ |
| | | +------------+ | | +------------+ |
| | | | cell 1 | | | | cell 2 | |
| | | +------------+ | | +------------+ |
| +------------+ | | +------------+ | | +------------+ |
| | cell 1 | | | | cell 2 | | | | cell 3 | |
| +------------+ | | +------------+ | | +------------+ |
+----------------+ +----------------+ +----------------+
答案 0 :(得分:13)
我已经创建了一个新的示例解决方案,因为之前的答案并不过时,不适合现代使用。
最新技术使用autolayout和self-sizing单元格,因此之前的答案将不再有效。我重新设计了解决方案以使用现代功能,并创建了一个示例项目来放置GitHub。
而不是计算每行的高度,这会导致额外的工作,而是代码获取最后一行的帧,以便可以计算从顶部开始的内容插入。它利用了表视图已经在做的事情,因此不需要额外的工作。
此代码也仅设置顶部插入,以防为键盘或其他叠加设置底部插入。
请报告GitHub上的任何错误或提交改进,我将更新此示例。
GitHub:https://github.com/brennanMKE/BottomTable
- (void)updateContentInsetForTableView:(UITableView *)tableView animated:(BOOL)animated {
NSUInteger lastRow = [self tableView:tableView numberOfRowsInSection:0];
NSUInteger lastIndex = lastRow > 0 ? lastRow - 1 : 0;
NSIndexPath *lastIndexPath = [NSIndexPath indexPathForItem:lastIndex inSection:0];
CGRect lastCellFrame = [self.tableView rectForRowAtIndexPath:lastIndexPath];
// top inset = table view height - top position of last cell - last cell height
CGFloat topInset = MAX(CGRectGetHeight(self.tableView.frame) - lastCellFrame.origin.y - CGRectGetHeight(lastCellFrame), 0);
UIEdgeInsets contentInset = tableView.contentInset;
contentInset.top = topInset;
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState;
[UIView animateWithDuration:animated ? 0.25 : 0.0 delay:0.0 options:options animations:^{
tableView.contentInset = contentInset;
} completion:^(BOOL finished) {
}];
}
答案 1 :(得分:10)
每当添加一行时调用此方法:
- (void)updateContentInset {
NSInteger numRows=[self tableView:_tableView numberOfRowsInSection:0];
CGFloat contentInsetTop=_tableView.bounds.size.height;
for (int i=0;i<numRows;i++) {
contentInsetTop-=[self tableView:_tableView heightForRowAtIndexPath:[NSIndexPath indexPathForItem:i inSection:0]];
if (contentInsetTop<=0) {
contentInsetTop=0;
break;
}
}
_tableView.contentInset = UIEdgeInsetsMake(contentInsetTop, 0, 0, 0);
}
答案 2 :(得分:5)
您可以在表格视图中设置标题,并使其足够高以推动第一个单元格。然后相应地设置tableView的contentOffset。我不认为有一种快速的内置方式可以做到这一点。
答案 3 :(得分:2)
这可以通过快速使用以下功能来完成
func updateContentInsetForTableView(tblView: UITableView, animated: Bool) {
let lastRow: NSInteger = self.tableView(tblView, numberOfRowsInSection: 0)
let lastIndex: NSInteger = lastRow > 0 ? lastRow - 1 : 0
let lastIndexPath: NSIndexPath = NSIndexPath(forRow: lastIndex, inSection: 0)
let lastCellFrame: CGRect = tblView.rectForRowAtIndexPath(lastIndexPath)
let topInset: CGFloat = max(CGRectGetHeight(tblView.frame) - lastCellFrame.origin.y - CGRectGetHeight(lastCellFrame), 0)
var contentInset: UIEdgeInsets = tblView.contentInset
contentInset.top = topInset
let option: UIViewAnimationOptions = UIViewAnimationOptions.BeginFromCurrentState
UIView.animateWithDuration(animated ? 0.25 : 0.0, delay: 0.0, options: option, animations: { () -> Void in
tblView.contentInset = contentInset
}) { (_) -> Void in }
}
答案 4 :(得分:2)
我不喜欢空单元格,基于contentInset
或transform
的解决方案,而是提出了其他解决方案:
UITableView
的布局是私密的,如果Apple愿意,可能会发生变化,最好是完全控制,从而使您的代码面向未来,更加灵活。我切换到UICollectionView
并根据UICollectionViewFlowLayout
实现了特殊布局(Swift 3):
override func layoutAttributesForElements(in rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
// Do we need to stick cells to the bottom or not
var shiftDownNeeded = false
// Size of all cells without modifications
let allContentSize = super.collectionViewContentSize()
// If there are not enough cells to fill collection view vertically we shift them down
let diff = self.collectionView!.bounds.size.height - allContentSize.height
if Double(diff) > DBL_EPSILON {
shiftDownNeeded = true
}
// Ask for common attributes
let attributes = super.layoutAttributesForElements(in: rect)
if let attributes = attributes {
if shiftDownNeeded {
for element in attributes {
let frame = element.frame;
// shift all the cells down by the difference of heights
element.frame = frame.offsetBy(dx: 0, dy: diff);
}
}
}
return attributes;
}
它适用于我的案例,显然,可以通过某种方式缓存内容大小高度来优化。另外,我不确定如果没有对大数据集进行优化会如何执行,我没有测试。我已经将示例项目与demo:MDBottomSnappingCells放在一起。
这是Objective-C版本:
- (NSArray *)layoutAttributesForElementsInRect:(CGRect)rect;
{
// Do we need to stick cells to the bottom or not
BOOL shiftDownNeeded = NO;
// Size of all cells without modifications
CGSize allContentSize = [super collectionViewContentSize];
// If there are not enough cells to fill collection view vertically we shift them down
CGFloat diff = self.collectionView.bounds.size.height - allContentSize.height;
if(diff > DBL_EPSILON)
{
shiftDownNeeded = YES;
}
// Ask for common attributes
NSArray *attributes = [super layoutAttributesForElementsInRect:rect];
if(shiftDownNeeded)
{
for(UICollectionViewLayoutAttributes *element in attributes)
{
CGRect frame = element.frame;
// shift all the cells down by the difference of heights
element.frame = CGRectOffset(frame, 0, diff);
}
}
return attributes;
}
答案 5 :(得分:2)
以下是@Brennan接受的解决方案的Swift 3版本,已经过测试和批准:)
func updateContentInsetForTableView( tableView:UITableView,animated:Bool) {
let lastRow = tableView.numberOfRows(inSection: 0)
let lastIndex = lastRow > 0 ? lastRow - 1 : 0;
let lastIndexPath = IndexPath(row: lastIndex, section: 9)
let lastCellFrame = tableView.rectForRow(at: lastIndexPath)
let topInset = max(tableView.frame.height - lastCellFrame.origin.y - lastCellFrame.height, 0)
var contentInset = tableView.contentInset;
contentInset.top = topInset;
_ = UIViewAnimationOptions.beginFromCurrentState;
UIView.animate(withDuration: 0.1, animations: { () -> Void in
tableView.contentInset = contentInset;
})
}
答案 6 :(得分:1)
使用这个。当然这会有所帮助。
- (void)reloadData
{
[super reloadData];
[self recalculateContentInset];
[self recalculateScrollIndicator];
}
- (void)recalculateContentInset
{
CGFloat contentInsetHeight = MAX(self.frame.size.height - self.contentSize.height, 0);
CGFloat duration = 0.0;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
[self setContentInset:UIEdgeInsetsMake(contentInsetHeight, 0, 0, 0)];
}completion:nil];
}
- (void)recalculateScrollIndicator
{
if(self.contentSize.height >= self.frame.size.height){
[self setShowsVerticalScrollIndicator:YES];
} else {
[self setShowsVerticalScrollIndicator:NO];
}
}
答案 7 :(得分:0)
if(indexPath.row!=CategoriesArray.count-1)
{
cell.hidden = YES;
}
return cell;
答案 8 :(得分:0)
我会根据单元格的数量调整UITableView并将其重新定位在其父视图中。我想这是解决方案涉及最小的解决方法。 另外,你真的需要使用UITableView吗?
答案 9 :(得分:0)
在新的部分添加一个空白单元格,并将其作为索引零处的部分。
-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
return 2;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if (section==0)
{
return 1;
}
return [self.yourArray count];
}
现在在
tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath //method add following in beginnig//
if (indexPath.section == 0 )
{
UITableViewCell * cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
// cell.backgroundColor = [UIColor clearColor];
return cell;
}
现在
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.section == 0 )
{
if (self.yourArray.count>0)
{
CGFloat totalCellHeight = self.messages.count * yourCellHeight;
if (totalCellHeight>= self.table.bounds.size.height)
{
return 0;
}
return self.table.bounds.size.height - totalCellHeight;
}
else
return self.table.bounds.size.height;
}
return yourCellHeight;
}
现在将其粘贴到重新加载tableView
的位置[self.table reloadData];
[self.table scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:[self.yourArray count]-1 inSection:1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
它对我有用。希望这会有所帮助。
答案 10 :(得分:0)
没有一行代码的优雅而快速的解决方案。
使用容器视图并将UITableViewController放入容器(嵌入segue)。
您可以为此容器设置所需的任何高度。
答案 11 :(得分:0)
我发现这样做的最佳方法是观察tableview的内容大小,并在必要时调整插入。例如:
static char kvoTableContentSizeContext = 0;
- (void) viewWillAppear:(BOOL)animated
{
[_tableView addObserver:self forKeyPath:@"contentSize" options:0 context:&kvoTableContentSizeContext];
}
- (void) viewWillDisappear:(BOOL)animated
{
[_tableView removeObserver:self forKeyPath:@"contentSize"];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (context == &kvoTableContentSizeContext) {
CGFloat contentHeight = _tableView.contentSize.height;
CGFloat tableHeight = _tableView.frame.size.height;
if (contentHeight < tableHeight) {
UIEdgeInsets insets = _tableView.contentInset;
insets.top = tableHeight - contentHeight;
_tableView.contentInset = insets;
} else {
_tableView.contentInset = UIEdgeInsetsZero;
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
答案 12 :(得分:0)
所有答案都有动态rowHeight和/或动画的怪癖。 对我来说,最好的解决方案是转换表格(flipY):
cellForRowAt
在cell.contentView.transform = CGAffineTransform (scaleX: 1,y: -1)
cell.accessoryView?.transform = CGAffineTransform (scaleX: 1,y: -1)
内:
public AudioTrack create() {
if (mTrack != null) {
if (mTrack.getState() == AudioTrack.STATE_INITIALIZED) {
return mTrack;
} else {
mTrack.release();
mTrack = null;
}
}
mBuffSize = AudioTrack.getMinBufferSize(Settings.SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
mTrack = new AudioTrack(AudioManager.STREAM_MUSIC, Settings.SAMPLE_RATE,
AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, mBuffSize, AudioTrack.MODE_STREAM);
if (mTrack.getState() == AudioTrack.STATE_INITIALIZED) {
mTrack.play();
return mTrack;
}
return null;
}
您也可以反转数据数组,也可以翻转section-header / footer。 你的页脚也会成为你的新标题 - 但是,嘿,它有效。
答案 13 :(得分:0)
func updateContentInsetForTableView( tableView:UITableView,animated:Bool) {
let lastRow = tableView.numberOfRows(inSection: 0)
let lastIndex = lastRow > 0 ? lastRow - 1 : 0;
let lastIndexPath = IndexPath(row: lastIndex, section: 9)
let lastCellFrame = tableView.rectForRow(at: lastIndexPath)
let topInset = max(tableView.frame.height - lastCellFrame.origin.y - lastCellFrame.height, 0)
var contentInset = tableView.contentInset;
contentInset.top = topInset;
_ = UIViewAnimationOptions.beginFromCurrentState;
UIView.animate(withDuration: 0.1, animations: { () -> Void in
tableView.contentInset = contentInset;
})
if self.commesnts.count > 0 {
tableView.scrollToRow(at: IndexPath(item:self.commesnts.count-1, section: 0), at: .bottom, animated: true)
}
}
我使用了@bkokot解决方案。它做了两件事
1. Start showing cells from bottom of UITableView
2. Scroll cells to bottom so that last inserted row become visible
(just like chat)