如何在iPhone上以编程方式增加UITableView单元格的高度?

22

我为显示从服务器JSON响应中获取的数据而拥有单独的自定义UITableViewCell。在每个UITableViewCell中,我都有一个“阅读更多”按钮。如果用户点击“阅读更多”按钮,我希望通过编程方式添加UILabel以显示来自服务器的其他信息。但最初我设置了UITableViewCell高度,因此在点击“阅读更多”按钮后,我无法看到附加的信息UILabel

以下是屏幕截图:

enter image description here

这是我需要的屏幕:

enter image description here

以下是我使用的代码:

-(NSInteger) numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{

   int height1;
    if(readMore){
        height1=200;
        NSLog(@"Clicked");
    }
    else{
        height1=100;
        NSLog(@"Not clicked");
    }
    return height1; // Normal height
}


-(NSInteger) tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section
{
    return [TitleArr  count];
}


- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
    {
        static NSString *simpleTableIdentifier = @"SimpleTableCell_iPad";

        cell = (TableCell_Leads *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    }
    else{
        static NSString *simpleTableIdentifier = @"TableCell_Leads";

        cell = (TableCell_Leads *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
    }
    if (cell == nil)
    {
        if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        {
            NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"SimpleTableCell_iPad" owner:self options:nil];
            cell = [nib objectAtIndex:0];

        }
        else{
            NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"TableCell_Leads" owner:self options:nil];
            cell = [nib objectAtIndex:0];
        }
    }




    cell.labTitle.text = [TitleArr objectAtIndex:indexPath.row];

    cell.labCategory.text=[CategoryArr objectAtIndex:indexPath.row];

    [cell.btnReadmore addTarget:self
                         action:@selector(funReadmore:)
               forControlEvents:UIControlEventTouchUpInside];


    return cell;
}



 - (IBAction)funReadmore:(id)sender
    {
        [self.tableView beginUpdates];
        readMore=TRUE;



        NSLog(@"READ MORE");
        [self.tableView endUpdates];
}

请给我建议。这是非常紧急的需求。 - Kalai
1
看看我的样例项目。 - Shankar BS
6个回答

26

首先,定义一个 bool 和一个 int 变量。

BOOL isReadMoreButtonTouched = NO;
int indexOfReadMoreButton = -1;

然后使用您的代码实现以下内容

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    [[cell btnReadmore] setTag:[indexPath row]];

    if(isReadMoreButtonTouched && [indexPath row]== indexOfReadMoreButton)
    {
       // design your read more label here
    }
}

现在实现IBAction

-(IBAction) funReadmore:(id)sender
{
    UIButton *readMoreButton = (UIButton *)sender;
    indexOfReadMoreButton=[readMoreButton tag];
    isReadMoreButtonTouched=YES;

    [[self tableView] beginUpdates];
    [[self tableView] reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForItem: indexOfReadMoreButton inSection:0]] withRowAnimation:UITableViewRowAnimationAutomatic];
    [[self tableView] endUpdates];
}

现在来看heightForRowAtIndexPath

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(isReadMoreButtonTouched && [indexPath row]== indexOfReadMoreButton) return 200.0f;
    else return 100.0f;
}

希望它对你有用。

是的,谢谢您的建议,我已经修改了我的代码(就像您发布的那样),但是uitableview单元格高度没有改变。 - Kalai
你的 heightForRowAtIndexPath 在点击“阅读更多”按钮后被调用了吗? - Tapas Pal
我想知道为什么在按下按钮时没有调用heightForRowAtIndexPath方法的原因是什么? - Kalai
好的,你能否仅使用reload重新加载你的表视图,而不是使用reloadRowsAtIndexPaths更新特定行?然后检查一下是否调用了你的heightForRowAtIndexPath方法。 - Tapas Pal
我把 reloadRowsAtIndexPaths 改成了 reloadData。但是当我选择“阅读更多”按钮时,heightForRowAtIndexPath 方法从未被调用。 - Kalai
显示剩余2条评论

4

int readMoreAtIndex; 作为你的类变量。在init方法 和/或 viewDidLoad/viewWillAppear 中将其初始化为负数。一些基本逻辑可能是这样的:

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{   
    if(readMoreAtIndex == indexPath.row) {
        return 400; //return as per your requirement
    }
    return 100;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    //same lines as currently you are doing to setup cell.     

    //important line
    [cell.btnReadmore setTag:indexPath.row];

    [cell.btnReadmore addTarget:self
                         action:@selector(funReadmore:)
               forControlEvents:UIControlEventTouchUpInside];

    if(indexPath.row == readMoreAtIndex) {
        //setup your cell according to your logic to show expanded view
    }
    else {
        //you are reusing cells, so provide logic to disappear shown expanded view if you want
    }

    return cell;
}

- (IBAction)funReadmore:(id)sender
{     
    UIButton *button = (UIButton *)sender;
    readMoreAtIndex = button.tag;
    [yourTableView reloadData];
    NSLog(@"READ MORE");
}

编辑: 实现可展开/折叠的表格视图教程链接。

  1. 可展开/折叠的TableView章节
  2. iOS可折叠表格视图

抱歉给您带来不便。我正在使用您的代码,但它并不能满足我的需求... :( - Kalai
你的问题需要修改,请说明如何展开视图? - Salman Zaidi
你能提供一些关于我的需求的示例或教程吗? - Kalai
我在我的一个项目中使用了同样类型的代码,顺利运行。顺便说一下,我正在编辑答案以添加教程和有用的材料链接。 - Salman Zaidi
谢谢你的教程链接... :) - Kalai

2

我发现了另一个基于自适应表格视图单元的解决方案。我们可以通过更新约束优先级来代替更新单元的高度(硬编码)。

fileprivate extension Int {
   static let rowHeight = 175
}

class CellArticleData {
  var article: Article
  var isExpanded: Bool

  init(article: Article, isExpanded: Bool) {
    self.article = article
    self.isExpanded = isExpanded
  }
}

 enum Article: String {
    case medicine, sport
    static let allArticles: [Article] = [.medicine, .sport]
    var title: String { return self.rawValue.capitalized }
    var description: String {
        switch self {
          case .medicine:
            return "Lorem Ipsum is simply dummy text of the printing and 
            typesetting industry"
          case .sport:
            return "Contrary to popular belief, Lorem Ipsum is not simply 
            random text. It has roots in a piece of classical Latin 
            literature from 45 BC, making it over 2000 years old. Richard 
            McClintock, a Latin professor at Hampden-Sydney College in 
            Virginia."
      }
   }
}

class ViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!
var articles: [CellArticleData] = Article.allArticles.map { CellArticleData(article: $0, isExpanded: false) }

override func viewDidLoad() {
    super.viewDidLoad()

    tableView.dataSource = self
    tableView.delegate = self
    tableView.rowHeight = UITableViewAutomaticDimension
    tableView.estimatedRowHeight = CGFloat(.rowHeight)
}

 extension ViewController: UITableViewDataSource {

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MyCell", for: indexPath) as! MyCell
    let articleData = articles[indexPath.row]
    cell.setup(articleData: articleData)
    cell.delegate = self
    return cell
  }
}

extension ViewController: MyCellDelegate {

  func handleReadMore() {
      tableView.reloadData()
   }
}

我有一个自定义类来表示单元格"MyCell",它处理协议和约束的更新:
protocol MyCellDelegate {
    func handleReadMore()
}

class MyCell: UITableViewCell {

   @IBOutlet weak var topConstraint: NSLayoutConstraint!
   @IBOutlet weak var bottomConstraint: NSLayoutConstraint!
   @IBOutlet weak var heightConstraint: NSLayoutConstraint!

  func setup(articleData: CellArticleData) {
    self.articleData = articleData

    titleLabel.text = articleData.article.title
    descriptionLabel.text = articleData.article.description
    readMoreLabel.isUserInteractionEnabled = true

    let readMoreTap = UITapGestureRecognizer(target: self, action: #selector(handleReadMore))
    readMoreTap.cancelsTouchesInView = false
    readMoreLabel.addGestureRecognizer(readMoreTap)
    updateCellConstraints()
}

fileprivate func updateCellConstraints() {
    if let articleData = self.articleData {
        if !articleData.isExpanded {
            heightConstraint.priority = 999
            topConstraint.priority = 250
            bottomConstraint.priority = 250
        }else {
            heightConstraint.priority = 250
            topConstraint.priority = 999
            bottomConstraint.priority = 999
        }

    }
  }

   func handleReadMore() {
      if let articleData = self.articleData {
         articleData.isExpanded = !articleData.isExpanded
         delegate?.handleReadMore(articleData: articleData)
     }
   }
}

这里有一个示例,展示了它的外观: 我的自定义单元格MyCell

1
你需要加入某种标记机制并管理高度。
  - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

最好、最理想的方法是根据文本计算高度,然后返回高度。
 - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{       
    if(readMore){
      return 500;
     }else{
      return 100;
     }
}

如果您正在使用自动布局,则可以通过使用 sizeToFit 方法手动计算每个标签的大小以适应内容。

我应该把这些代码放在哪里? - Kalai
如果(readMore)最初被调用。但是当我按下“阅读更多”按钮时,高度不会改变 :( - Kalai
这是一个非常紧急的需求..请给我一些简要的解决方案。 - Kalai
是的,我编辑了我的问题。您能否提供一些关于我的需求的示例或教程? - Kalai

1
我发布了一个示例代码,可在按钮点击时展开单元格,文本大小适用于iOS6和iOS7,这只是示例代码,请查看此代码可能会有帮助... :)
这只是一个可以尝试的示例项目。


     in customCell.h
     #import <UIKit/UIKit.h>
     @class CustomCell;
     @protocol ButtonClickDelegate <NSObject> //custom delegate
     - (void)whenReadMoreButtonClicked:(CustomCell *)cell;//i am passing the cell itself
     @end

     @interface CustomCell : UITableViewCell
     @property (nonatomic,assign)id<ButtonClickDelegate>delegate;
    @property (nonatomic,retain)UILabel *mesageLabel;
    @property (nonatomic,retain)NSString *message;
    @property (nonatomic,assign)BOOL expand;

    @end

在 customCell.m 中


    #import "CustomCell.h"

    @implementation CustomCell
    @synthesize delegate;//synthesize it
    @synthesize mesageLabel;
    @synthesize message;
    @synthesize expand;


    - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
   {
      self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
      if (self) {
     // Initialization code
      UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(5,2, 100, 35)];
      [button addTarget:self action:@selector(whenButtonClicked:) forControlEvents:UIControlEventTouchUpInside];
      [button setTitle:@"Read More" forState:UIControlStateNormal];
       button.backgroundColor = [UIColor greenColor];

      self.mesageLabel = [[UILabel alloc]initWithFrame:CGRectMake(0 , 40,0 ,0)];
       self.mesageLabel.backgroundColor = [UIColor redColor];
      self.mesageLabel.numberOfLines = 100;
      [self addSubview:self.mesageLabel];
       [self addSubview:button];
    }
     return self;
   }

  - (void)setSelected:(BOOL)selected animated:(BOOL)animated
  {
       [super setSelected:selected animated:animated];

      // Configure the view for the selected state
  }

   - (void)whenButtonClicked:(id)sender
   {
       if([self.delegate respondsToSelector:@selector(whenReadMoreButtonClicked:)])
       {
          [self.delegate whenReadMoreButtonClicked:self];//delegate to controller
       }

  }

  - (void)layoutSubviews
  {
       [super layoutSubviews];
      self.mesageLabel.text = self.message;
      if(self.expand)
      {
          CGSize size = [self findMessgeStringHeight];
          self.mesageLabel.frame = CGRectMake(0, 40, size.width, size.height);
      }
      else
      {
          self.mesageLabel.frame = CGRectMake(0, 40, self.bounds.size.width, 100);
      }

  }


   //helper method to find height
    - (CGSize)findMessgeStringHeight
     {  
         NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:self.message attributes:@{ NSFontAttributeName:[UIFont systemFontOfSize:17.0f] }];
        CGRect rect = [attributedText boundingRectWithSize:(CGSize){225, MAXFLOAT}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
        CGSize requiredSize = rect.size;

         return requiredSize; //finally u return your height
     } 

     @end


在视图控制器中,保留HTML标记`

`和`
`。

      #import "ViewController.h"
      #import "CustomCell.h"

      @interface ViewController ( <UITableViewDataSource,UITableViewDelegate,ButtonClickDelegate>//confirm's to delegate
      {

        BOOL ButtonClickedForExpand;
        NSMutableArray *array;

     }

     @property (nonatomic,retain)NSIndexPath *previousIndexPath;
      @property (nonatomic,retain)NSIndexPath *currentIndexPath;

     @end

    @implementation ViewController
     @synthesize previousIndexPath;
    @synthesize currentIndexPath;

    - (void)viewDidLoad
     {
         [super viewDidLoad];
        ButtonClickedForExpand = NO;
// Do any additional setup after loading the view, typically from a nib.
      array = [[NSMutableArray alloc]initWithObjects:@"hello happy coding some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext",@"some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext",@"ello happy coding some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext ello happy coding some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext ello happy coding some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext ello happy coding some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext ello happy coding some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtext some longtextsome longtext some longtext", nil];
      }

    - (void)didReceiveMemoryWarning
    {
       [super didReceiveMemoryWarning];
      // Dispose of any resources that can be recreated. 
    }

    - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
   {
       return 1;
   }

    - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
   {
      return array.count;
   }
     - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  { 
      CustomCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CELL"];
     if(cell == nil)
      {
        cell = [[CustomCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"CELL"];
      }

     if(ButtonClickedForExpand)
     {
        if(indexPath.row == currentIndexPath.row)
        {
           cell.expand = YES;
        }
        else
       {
          cell.expand = NO;
       }
    }
    else
   {
       cell.expand = NO;
   }

    cell.message = [array objectAtIndex:indexPath.row];
    cell.delegate = self;//u need to set delegate to self
    return cell;
  }

   - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  {
      CGSize size = [self findMessgeStringHeight:[array objectAtIndex:indexPath.row]];
     if(ButtonClickedForExpand)
      {
         if(indexPath.row == currentIndexPath.row)
         {
             return size.height + 30;
         }
        else
        {
           return 100;//by default
        }
    }
    else
     {
         return 100;
     }

  } 

    //helper function to return the correct height for your label
  - (CGSize)findMessgeStringHeight:(NSString *)str
  {

     NSAttributedString *attributedText = [[NSAttributedString alloc] initWithString:str attributes:@{ NSFontAttributeName:[UIFont systemFontOfSize:17.0f] }];
     CGRect rect = [attributedText boundingRectWithSize:(CGSize){225, MAXFLOAT}
                                           options:NSStringDrawingUsesLineFragmentOrigin
                                           context:nil];
     CGSize requiredSize = rect.size;

     return requiredSize; //finally u return your height
  }



  - (void)whenReadMoreButtonClicked:(CustomCell *)cell
 {
       ButtonClickedForExpand = YES;
       self.previousIndexPath = self.currentIndexPath;
      self.currentIndexPath = [self.tableView indexPathForCell:cell];


       [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:self.currentIndexPath] withRowAnimation:UITableViewRowAnimationFade];

    if(self.previousIndexPath.row == nil)
     { 
      return;
     }
     else
     {
       [self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:self.previousIndexPath] withRowAnimation:UITableViewRowAnimationFade];
      }

   }


   @end


编辑:添加了ButtonClickedForExpand用于第一次点击

编辑2:在视图控制器的"whenReadMoreButtonClicked"方法中更改了if(self.previousIndexPath.row == nil)

如果不理解,请评论


1
我建议你按照以下步骤操作:
在自定义单元格中,将可用的内容放入隐藏的UIView容器中。因此,默认情况下不可见。
当“阅读更多”按钮被按下时,在绘制tableView的类中处理其事件触发,就像您正在处理“funReadmore”处理程序一样。
获取单元格的索引并将其管理/添加到NSMutableArray对象中。
使用以下代码重新加载TableView数据:
[yourTableViewInstance reloadData];
在“heightForRowAtIndexPath”委托函数中编写如下代码:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { if(arrayOfExpandedCellIndexes.contains(indexPath.row)) return EXTENDED_CELL_HEIGHT; // 宏:#define EXTENDED_CELL_HEIGHT 230.0f else return NORMAL_CELL_HEIGHT; // 宏:#define NORMAL_CELL_HEIGHT 100.0f }
使用这种方法,您可以处理多个已按“阅读更多”按钮的单元格。如果您的要求只能展开一个单元格,请使用以下代码清除您的“arrayOfExpandedCellIndexes”:
[arrayOfExpandedCellIndexes removeAllObjects];
注意:一旦调整了单元格的高度,请不要忘记将隐藏的视图显示出来。

希望这有所帮助!


在尝试扩展单元格大小时,这种方法对我没有起作用。 - NSNoob
@NSNoob,你能告诉我你得到了什么吗?我已经使用了这种方法,它运行良好。 - NeverHopeless
我这样做是为了增加包含章节/书籍标题的单元格大小。http://pastebin.com/HRfEpseg 对于第一个单元格(第一本书的标题)效果很好。但是其余的,情况并不如计划的那样。 - NSNoob
几个问题:1)当单元格应该展开时,您是否处理了UIView的可见性?2)您确定在heightForRowAtIndex函数中引用了正确的数据源吗?3)您如何在数据源中存储值?我直接存储了indexPath.row(一个整数),而您正在使用NSString。您能否澄清一下这些问题? - NeverHopeless
我并没有像“点击展开”那样去做。我是在创建表格时针对特定单元格增加了单元格大小,例如章节标题和书名比普通单元格要大。而且,是的,数据源是正确的,它们包含主数组的索引,其中包含章节名称和书名。这就是为什么我使用字符串,因为索引被存储为字符串。 - NSNoob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接