【问题标题】:How to implement an accordion view for an iPhone SDK app?如何为 iPhone SDK 应用程序实现手风琴视图?
【发布时间】:2010-12-29 00:03:11
【问题描述】:

有没有人看过 iPhone 的“手风琴”(可能称为“动画轮廓”)视图的实现?我找到了一个 Cocoa 的示例项目,但在尝试移植之前,我希望有人已经发明了轮子。

为了清楚起见,在 UIView 中,考虑一堆部分,每个部分包含一个标题,然后是一些内容。当用户触摸标题(或通过某些消息/事件)时,如果该部分已经打开 => 关闭它;如果该部分已关闭 => 打开它并关闭任何其他打开的部分。 jQuery 中的示例如下所示: http://docs.jquery.com/UI/Accordion

就我而言,我希望能够将任何 UIView 内容放在每个部分中。

我很想看看一些真正的应用程序已经实现了这一点 - 只是想知道这是可能的!

【问题讨论】:

  • 看看github.com/nacho4d/Accordion它为iPad实现了手风琴风格的导航,在那里你可以看到一切是如何运作的!
  • @Jacob 并非一切皆有可能!见stackoverflow.com/questions/4962539/…。 ;)
  • 这个 Accordion 样本具有真正的潜力。我试了一下,发现它通过使用 UITableView 单元格提供了非常平滑的过渡和手风琴感觉。我建议更改 - (void) fileAccordionDatasourceManager:(N4FileAccordionDatasourceManager *) manager didInsertRowsAtIndexPaths:(NSArray *)indexPaths{ //[self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationLeft]; [self.tableView insertRowsAtIndexPaths:indexPaths withRowAnimation: UITableViewRowAnimationTop];
  • 这是完美的答案。效果很好....

标签: iphone objective-c iphone-sdk-3.0 uiview


【解决方案1】:

我只会使用 UITableView,让每个单元格的高度取决于它是否“打开”,然后从那里开始。调整行的大小很容易,您只需将组合单元格的总高度设置为 UITableView 中的可用高度,使其看起来更像手风琴,而不仅仅是表格。

这是一个应该有效的快速技巧,但在您的 UITableViewController 子类的 .h 文件中:

bool sectionopen[4]; ///or some other way of storing the sections expanded/closed state

然后在 .m 文件中添加如下内容:

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

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return 4;
}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath  {
    if (sectionopen[indexPath.row]) {
        return 240;///it's open
    } else {
        return 45;///it's closed
    }

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *mycell = [[[UITableViewCell alloc] init] autorelease];
    mycell.textLabel.text= @"Section Name";
    return mycell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    ///turn them all off
    sectionopen[0]=NO;
    sectionopen[1]=NO;
    sectionopen[2]=NO;
    sectionopen[3]=NO;

    ///open this one
    sectionopen[indexPath.row]=YES;

    ///animate the opening and expand the row
    [self.tableView reloadSections:[NSIndexSet indexSetWithIndex:0] withRowAnimation:UITableViewRowAnimationFade];
}

这基本上会占用 4 行并将它们转换为可折叠部分,其中选择一行会将其展开为 240 像素并将所有其他行折叠为 40。您可以更改所有这些数字并计算出这些部分并执行其他任何操作喜欢它。

我已经尝试过了,它确实有效。然后,您可以通过将其他内容添加到您的单元格创建代码来完成它,以将您想要的任何内容添加到一个部分(如果您愿意,可能包括一个滚动的 UITextView)。

【讨论】:

    【解决方案2】:

    我只是偶然发现了这一点,发现 mjdth 的解决方案非常直接且很有帮助。但是,您可能想使用

    [self.tableView reloadRowsAtIndexPaths: paths withRowAnimation:UITableViewRowAnimationBottom];
    

    而不是建议的 reloadSections 方法,因为重新加载行可以让您实现更平滑的过渡。

    【讨论】:

      【解决方案3】:

      这是我目前正在使用的CollapsingTableViewDelegate 类。这仅适用于静态表格内容。

      您向此类提供CollapsingTableCellDelegate 实现,该类必须知道如何计算每行的折叠和展开大小,以及如何为每行创建UIView。无论折叠还是展开,视图都保持不变,因此每行视图的顶部条用作该行的可点击标题。

      然后,您将此类作为您的UITableView 的数据源和委托。

      头文件CollapsingTableViewDelegate.h:

      #import <UIKit/UIKit.h>
      
      @protocol CollapsingTableCellDelegate<NSObject>
      
      @required
      - (CGFloat)collapsingCellHeightForRow:(int)row expanded:(BOOL)expanded;
      - (UIView *)collapsingCellViewForRow:(int)row;
      
      @optional
      - (BOOL)collapsingCellAllowCollapse:(int)row;
      
      @end
      
      struct cell;
      
      @interface CollapsingTableViewDelegate : NSObject <UITableViewDelegate, UITableViewDataSource> {
          id<CollapsingTableCellDelegate> cellDelegate;
          int numCells;
          int currentSelection;
          struct cell *cells;
      }
      
      @property (nonatomic, retain, readonly) id<CollapsingTableCellDelegate> cellDelegate;
      @property (nonatomic, assign, readonly) int numCells;
      @property (nonatomic, assign) int currentSelection;
      @property (nonatomic, assign, readonly) struct cell *cells;
      
      - (CollapsingTableViewDelegate *)initWithCellDelegate:(id<CollapsingTableCellDelegate>)delegate numCells:(int)numCells;
      - (void)tableView:(UITableView *)tableView touchRow:(int)newSelection;
      
      @end
      

      和源文件CollapsingTableViewDelegate.m:

      #import "CollapsingTableViewDelegate.h"
      
      @implementation CollapsingTableViewDelegate
      
      struct cell {
          u_char expanded;
          u_char collapsable;
      };
      
      @synthesize cellDelegate;
      @synthesize currentSelection;
      @synthesize cells;
      @synthesize numCells;
      
      #pragma mark -
      #pragma mark Setup and Teardown
      
      - (CollapsingTableViewDelegate *)initWithCellDelegate:(id<CollapsingTableCellDelegate>)delegate numCells:(int)num {
          if ([super init] == nil)
              return nil;
          if ((cells = calloc(num, sizeof(*cells))) == NULL) {
              [self autorelease];
              return nil;
          }
          cellDelegate = [delegate retain];
          numCells = num;
          for (int row = 0; row < self.numCells; row++) {
              struct cell *const cell = &self.cells[row];
      
              cell->collapsable = ![self.cellDelegate respondsToSelector:@selector(collapsingCellAllowCollapse:)]
                || [self.cellDelegate collapsingCellAllowCollapse:row];
              cell->expanded = !cell->collapsable;
          }
          currentSelection = -1;
          return self;
      }
      
      - (void)dealloc {
          [cellDelegate release];
          free(cells);
          [super dealloc];
      }
      
      - (void)tableView:(UITableView *)tableView reloadRow:(int)row fade:(BOOL)fade {
          [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:row inSection:0]]
                           withRowAnimation:fade ? UITableViewRowAnimationFade : UITableViewRowAnimationNone];
      }
      
      - (void)tableView:(UITableView *)tableView touchRow:(int)newSelection {
      
          // Sanity check
          if (newSelection < -1 || newSelection >= self.numCells) {
              NSLog(@"CollapsingTableViewDelegate: invalid row %d not in the range [-1..%d)", newSelection, self.numCells);
              return;
          }
      
          // Gather info
          int oldSelection = self.currentSelection;
          BOOL sameCellSelected = newSelection == oldSelection;
          struct cell *const oldCell = oldSelection != -1 ? &self.cells[oldSelection] : NULL;
          struct cell *const newCell = newSelection != -1 ? &self.cells[newSelection] : NULL;
      
          // Mark old cell as collapsed and new cell as expanded
          if (newCell != NULL)
              newCell->expanded = TRUE;
          if (oldCell != NULL)
              oldCell->expanded = FALSE;
          self.currentSelection = sameCellSelected ? -1 : newSelection;
      
          // Update table view
          if (oldSelection >= newSelection) {
              if (oldSelection != -1)
                  [self tableView:tableView reloadRow:oldSelection fade:sameCellSelected];
              if (newSelection != -1 && !sameCellSelected)
                  [self tableView:tableView reloadRow:newSelection fade:TRUE];
          } else {
              if (newSelection != -1 && !sameCellSelected)
                  [self tableView:tableView reloadRow:newSelection fade:TRUE];
              if (oldSelection != -1)
                  [self tableView:tableView reloadRow:oldSelection fade:sameCellSelected];
          }
      
          // If expanding a cell, scroll it into view
          if (newSelection != -1 && !sameCellSelected) {
              [tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:newSelection inSection:0]
                               atScrollPosition:UITableViewScrollPositionTop
                                       animated:TRUE];
          }
      }
      
      #pragma mark -
      #pragma mark Table view data source
      
      - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
          return 1;
      }
      
      - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
          return self.numCells;
      }
      
      - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
          int row = [indexPath row];
          struct cell *const cell = &self.cells[row];
          return [self.cellDelegate collapsingCellHeightForRow:row expanded:cell->expanded];
      }
      
      - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
          int row = [indexPath row];
          UIView *cellView = [self.cellDelegate collapsingCellViewForRow:row];
          [cellView removeFromSuperview];
          UITableViewCell *tvcell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil] autorelease];
          [tvcell.contentView addSubview:cellView];
          tvcell.clipsToBounds = TRUE;
          tvcell.selectionStyle = UITableViewCellSelectionStyleNone;
          return tvcell;
      }
      
      #pragma mark -
      #pragma mark Table view delegate
      
      - (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath {
          int row = [indexPath row];
          struct cell *const cell = &self.cells[row];
          return cell->collapsable ? indexPath : nil;
      }
      
      - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)newSelection {
          [tableView deselectRowAtIndexPath:newSelection animated:TRUE];
          [self tableView:tableView touchRow:[newSelection row]];
      }
      
      @end
      

      不完美,但似乎基本上对我有用。

      【讨论】:

        【解决方案4】:

        如果有人感兴趣,我在这里找到了这个例子。

        http://www.cocoanetics.com/2011/03/expandingcollapsing-tableview-sections/

        【讨论】:

          【解决方案5】:

          我发现的每个解决方案都使用 UITableView,这对我不起作用,因为我没有显示表格数据。这就是我创建AccordionView 控件的原因。用法很简单:

          AccordionView *accordion = [[AccordionView alloc] initWithFrame:CGRectMake(0, 0, 320, 420)];
          [self addSubview:accordion];
          
          // Only height is taken into account, so other parameters are just dummy
          UIButton *header1 = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 0, 30)];
          [header1.titleLabel setText:@"First row"];
          
          UIView *view1 = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 200)];
          // ... add subviews to view1
          
          [accordion addHeader:header1 withView:view1];
          
          // ... add more panels
          
          [accordion setSelectedIndex:0];
          

          在现实生活中是这样的:

          黑条是标题,您可以根据需要自定义它们(我使用的是 Three20)。

          【讨论】:

          • 嗨 Suda,感谢您的控制。我在使用它时遇到了一个小障碍。我在项目的 Github 上打开了一个问题 here。你能看一下吗?
          • 嗨,suda,非常感谢您的工作。这里我开了一个小问题。请看一看。 github.com/appsome/AccordionView/issues/23
          【解决方案6】:

          我找到了这段代码:手风琴的简单实现..

          https://github.com/kuon/ios-example-accordion

          我希望这会对某人有所帮助..

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-03-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-05-20
            • 1970-01-01
            • 2017-10-26
            • 1970-01-01
            相关资源
            最近更新 更多