I'd like to add a button to a table cell. The "Delete Event" in the calendar app inspired me... (a similar case is "Share Contact" in contacts)
我想在表格单元格中添加一个按钮。日历应用程序中的“删除事件”启发了我...(类似案例是联系人中的“共享联系人”)
As of now there's
截至目前已有
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//..yadayadayada
cell = [tableView dequeueReusableCellWithIdentifier:@"buttonCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"buttonCell"] autorelease];
}
UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark];
[button setBackgroundColor:[UIColor redColor]];
button.titleLabel.text = @"Foo Bar";
[cell.contentView addSubview:button];
which produces a button, indeed. It doesn't look yet how it's supposed to (it's obvious I've never dealt with buttons in iPhone, yet), but is this at least the right approach?
确实产生了一个按钮。它看起来还不怎么样(显然我从来没有处理过iPhone中的按钮),但这至少是正确的方法吗?
5 个解决方案
#1
19
The way you have it now each time a cell is shown you're allocating a button, setting its value, and adding it to the cell's contentView. When the cell gets reused (via dequeueReusableCellWithIdentifier
) you'll be creating another new button, adding it to the cell (on top of the old one) etc. The fact that it's gone through addSubview
but no explicit release means each button's retain count will never go to zero so they'll all stick around. After a while of scrolling up and down the cell will end up with hundreds of button subviews which probably isn't what you want.
现在,每次显示一个单元格时,您都可以分配一个按钮,设置其值,并将其添加到单元格的contentView中。当单元格被重用时(通过dequeueReusableCellWithIdentifier)你将创建另一个新按钮,将它添加到单元格(在旧单元格之上)等。事实上它已通过addSubview但没有明确释放意味着每个按钮的保留计数将永远不会归零,所以他们都会坚持下去。经过一段时间的上下滚动,单元格最终会有数百个按钮子视图,这可能不是你想要的。
A few tips:
一些提示:
-
Never allocate stuff inside a
cellForRowAtIndexPath
call unless it's done whendequeueReusableCellWithIdentifier
is returning nil and you're initializing the cell. All other subsequent times you'll be handed back the cached cell that you will have already set up so all you have to do is change the labels or icons. You're going to want to move all that button allocation stuff up inside theif
conditional right after the cell allocation code.永远不要在cellForRowAtIndexPath调用中分配东西,除非在dequeueReusableCellWithIdentifier返回nil并且你正在初始化单元格时完成。所有其他后续时间您将被移交给您已经设置的缓存单元格,因此您只需更改标签或图标即可。您将要在单元格分配代码之后将所有按钮分配内容移动到if条件内。
-
The button needs to have a position and also a target set for it so it'll do something when tapped. If every cell is going to have this button a neat trick is to have them all point to the same target method but set the button's
tag
value to theindexPath.row
of the cell (outside the cell allocation block since it varies for each cell). The common tap handler for the button would use the tag value of the sender to look up the underlying data in the dataSource list.按钮需要有一个位置,也需要设置一个目标,以便在点击时执行某些操作。如果每个单元格都有这个按钮,一个巧妙的技巧是让它们都指向相同的目标方法,但将按钮的标记值设置为单元格的indexPath.row(在单元格分配块之外,因为它对于每个单元格而变化) 。按钮的公共点击处理程序将使用发送方的标记值来查找dataSource列表中的基础数据。
-
Call
release
on the button after you've done anaddSubview
. That way the retain count will fall to zero and the object will actually get released when the parent is released.完成addSubview后,在按钮上调用release。这样,保留计数将降至零,并且在释放父对象时实际上将释放对象。
-
Instead of adding the button via
addSubview
, you can return it as theaccessoryView
for the cell so you don't have to worry about positioning it (unless you're already using the accessoryView for something else -- like disclosure buttons).您可以将其作为单元的accessoryView返回,而不是通过addSubview添加按钮,因此您不必担心定位它(除非您已经将accessoryView用于其他内容 - 例如公开按钮)。
#2
7
I took a different approach to creating an equivalent to the 'Delete Event' button in the Calendar app. Rather than add a button as a subview, I added two background views (red and darker red, with nice gradients) to the cells and then rounded off the corners and set the border to grey.
我采用了不同的方法来创建日历应用程序中“删除事件”按钮的等效项。我没有添加按钮作为子视图,而是向单元格添加了两个背景视图(红色和深红色,具有漂亮的渐变),然后对角进行四舍五入并将边框设置为灰色。
The code below creates a re-usable cell (in the usual fashion). The two images referred to ('redUp.png' and 'redDown.png') were taken from a screenshot of the calendar's 'Delete Event' button. (That seemed quicker than creating the gradients programmatically.) There's scope for a bit more fine tuning to get it even closer to the Calendar's 'Delete Event' appearance, but this is pretty close.
下面的代码创建了一个可重用的单元格(以通常的方式)。引用的两个图像('redUp.png'和'redDown.png')取自日历的“删除事件”按钮的屏幕截图。 (这似乎比以编程方式创建渐变更快。)可以进行更微调,以使其更接近日历的“删除事件”外观,但这非常接近。
The button's action is triggered by the tableView delegate method tableView:didSelectRowAtIndexPath: method.
按钮的动作由tableView委托方法tableView:didSelectRowAtIndexPath:方法触发。
// create a button from a table row like the Calendar's 'Delete Event' button
// remember to have an #import <QuartzCore/QuartzCore.h> some above this code
static NSString *CellWithButtonIdentifier = @"CellWithButton";
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:CellWithButtonIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellWithButtonIdentifier] autorelease];
[[cell textLabel] setTextAlignment: UITextAlignmentCenter];
UIImageView* upImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redUp.png"]];
UIImageView* downImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redDown.png"]];
[cell setBackgroundView: upImage];
[cell setSelectedBackgroundView: downImage];
[[upImage layer] setCornerRadius:8.0f];
[[upImage layer] setMasksToBounds:YES];
[[upImage layer] setBorderWidth:1.0f];
[[upImage layer] setBorderColor: [[UIColor grayColor] CGColor]];
[[downImage layer] setCornerRadius:8.0f];
[[downImage layer] setMasksToBounds:YES];
[[downImage layer] setBorderWidth:1.0f];
[[downImage layer] setBorderColor: [[UIColor grayColor] CGColor]];
[[cell textLabel] setTextColor: [UIColor whiteColor]];
[[cell textLabel] setBackgroundColor:[UIColor clearColor]];
[cell setBackgroundColor:[UIColor clearColor]]; // needed for 3.2 (not needed for later iOS versions)
[[cell textLabel] setFont:[UIFont boldSystemFontOfSize:20.0]];
[upImage release];
[downImage release];
}
return cell;
#3
1
Yes, this is generally the correct approach.
是的,这通常是正确的方法。
A tip:
一个提示:
-
Set the callback for your button events, so that it actually does something when clicked.
设置按钮事件的回调,以便在单击时实际执行某些操作。
[myButton addTarget:self action:@selector(whatMyButtonShouldDo:) forControlEvents:UIControlEventTouchUpInside];
EDIT: Adjusted code for using a system button, which renders a lot of what I had written uneccessary.
编辑:调整使用系统按钮的代码,这使我写了很多不必要的东西。
#4
1
Yes, you are on the right track, that's the easiest way to add a subview to a cell (the other is subclassing a UITableViewCell
).
是的,你是在正确的轨道上,这是向子单元添加子视图的最简单方法(另一种是子类化UITableViewCell)。
Check the Apple guide for more info.
有关详细信息,请查看Apple指南。
#5
0
To avoid positioning and memory management hassles you can create a specialized cell class and link to a XIB. That sounds like the cleanest approach to me. Here is the link: http://icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/
为了避免定位和内存管理麻烦,您可以创建一个专门的单元类并链接到XIB。这听起来对我来说是最干净的方法。这是链接:http://icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/
#1
19
The way you have it now each time a cell is shown you're allocating a button, setting its value, and adding it to the cell's contentView. When the cell gets reused (via dequeueReusableCellWithIdentifier
) you'll be creating another new button, adding it to the cell (on top of the old one) etc. The fact that it's gone through addSubview
but no explicit release means each button's retain count will never go to zero so they'll all stick around. After a while of scrolling up and down the cell will end up with hundreds of button subviews which probably isn't what you want.
现在,每次显示一个单元格时,您都可以分配一个按钮,设置其值,并将其添加到单元格的contentView中。当单元格被重用时(通过dequeueReusableCellWithIdentifier)你将创建另一个新按钮,将它添加到单元格(在旧单元格之上)等。事实上它已通过addSubview但没有明确释放意味着每个按钮的保留计数将永远不会归零,所以他们都会坚持下去。经过一段时间的上下滚动,单元格最终会有数百个按钮子视图,这可能不是你想要的。
A few tips:
一些提示:
-
Never allocate stuff inside a
cellForRowAtIndexPath
call unless it's done whendequeueReusableCellWithIdentifier
is returning nil and you're initializing the cell. All other subsequent times you'll be handed back the cached cell that you will have already set up so all you have to do is change the labels or icons. You're going to want to move all that button allocation stuff up inside theif
conditional right after the cell allocation code.永远不要在cellForRowAtIndexPath调用中分配东西,除非在dequeueReusableCellWithIdentifier返回nil并且你正在初始化单元格时完成。所有其他后续时间您将被移交给您已经设置的缓存单元格,因此您只需更改标签或图标即可。您将要在单元格分配代码之后将所有按钮分配内容移动到if条件内。
-
The button needs to have a position and also a target set for it so it'll do something when tapped. If every cell is going to have this button a neat trick is to have them all point to the same target method but set the button's
tag
value to theindexPath.row
of the cell (outside the cell allocation block since it varies for each cell). The common tap handler for the button would use the tag value of the sender to look up the underlying data in the dataSource list.按钮需要有一个位置,也需要设置一个目标,以便在点击时执行某些操作。如果每个单元格都有这个按钮,一个巧妙的技巧是让它们都指向相同的目标方法,但将按钮的标记值设置为单元格的indexPath.row(在单元格分配块之外,因为它对于每个单元格而变化) 。按钮的公共点击处理程序将使用发送方的标记值来查找dataSource列表中的基础数据。
-
Call
release
on the button after you've done anaddSubview
. That way the retain count will fall to zero and the object will actually get released when the parent is released.完成addSubview后,在按钮上调用release。这样,保留计数将降至零,并且在释放父对象时实际上将释放对象。
-
Instead of adding the button via
addSubview
, you can return it as theaccessoryView
for the cell so you don't have to worry about positioning it (unless you're already using the accessoryView for something else -- like disclosure buttons).您可以将其作为单元的accessoryView返回,而不是通过addSubview添加按钮,因此您不必担心定位它(除非您已经将accessoryView用于其他内容 - 例如公开按钮)。
#2
7
I took a different approach to creating an equivalent to the 'Delete Event' button in the Calendar app. Rather than add a button as a subview, I added two background views (red and darker red, with nice gradients) to the cells and then rounded off the corners and set the border to grey.
我采用了不同的方法来创建日历应用程序中“删除事件”按钮的等效项。我没有添加按钮作为子视图,而是向单元格添加了两个背景视图(红色和深红色,具有漂亮的渐变),然后对角进行四舍五入并将边框设置为灰色。
The code below creates a re-usable cell (in the usual fashion). The two images referred to ('redUp.png' and 'redDown.png') were taken from a screenshot of the calendar's 'Delete Event' button. (That seemed quicker than creating the gradients programmatically.) There's scope for a bit more fine tuning to get it even closer to the Calendar's 'Delete Event' appearance, but this is pretty close.
下面的代码创建了一个可重用的单元格(以通常的方式)。引用的两个图像('redUp.png'和'redDown.png')取自日历的“删除事件”按钮的屏幕截图。 (这似乎比以编程方式创建渐变更快。)可以进行更微调,以使其更接近日历的“删除事件”外观,但这非常接近。
The button's action is triggered by the tableView delegate method tableView:didSelectRowAtIndexPath: method.
按钮的动作由tableView委托方法tableView:didSelectRowAtIndexPath:方法触发。
// create a button from a table row like the Calendar's 'Delete Event' button
// remember to have an #import <QuartzCore/QuartzCore.h> some above this code
static NSString *CellWithButtonIdentifier = @"CellWithButton";
UITableViewCell *cell = [self dequeueReusableCellWithIdentifier:CellWithButtonIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellWithButtonIdentifier] autorelease];
[[cell textLabel] setTextAlignment: UITextAlignmentCenter];
UIImageView* upImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redUp.png"]];
UIImageView* downImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"redDown.png"]];
[cell setBackgroundView: upImage];
[cell setSelectedBackgroundView: downImage];
[[upImage layer] setCornerRadius:8.0f];
[[upImage layer] setMasksToBounds:YES];
[[upImage layer] setBorderWidth:1.0f];
[[upImage layer] setBorderColor: [[UIColor grayColor] CGColor]];
[[downImage layer] setCornerRadius:8.0f];
[[downImage layer] setMasksToBounds:YES];
[[downImage layer] setBorderWidth:1.0f];
[[downImage layer] setBorderColor: [[UIColor grayColor] CGColor]];
[[cell textLabel] setTextColor: [UIColor whiteColor]];
[[cell textLabel] setBackgroundColor:[UIColor clearColor]];
[cell setBackgroundColor:[UIColor clearColor]]; // needed for 3.2 (not needed for later iOS versions)
[[cell textLabel] setFont:[UIFont boldSystemFontOfSize:20.0]];
[upImage release];
[downImage release];
}
return cell;
#3
1
Yes, this is generally the correct approach.
是的,这通常是正确的方法。
A tip:
一个提示:
-
Set the callback for your button events, so that it actually does something when clicked.
设置按钮事件的回调,以便在单击时实际执行某些操作。
[myButton addTarget:self action:@selector(whatMyButtonShouldDo:) forControlEvents:UIControlEventTouchUpInside];
EDIT: Adjusted code for using a system button, which renders a lot of what I had written uneccessary.
编辑:调整使用系统按钮的代码,这使我写了很多不必要的东西。
#4
1
Yes, you are on the right track, that's the easiest way to add a subview to a cell (the other is subclassing a UITableViewCell
).
是的,你是在正确的轨道上,这是向子单元添加子视图的最简单方法(另一种是子类化UITableViewCell)。
Check the Apple guide for more info.
有关详细信息,请查看Apple指南。
#5
0
To avoid positioning and memory management hassles you can create a specialized cell class and link to a XIB. That sounds like the cleanest approach to me. Here is the link: http://icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/
为了避免定位和内存管理麻烦,您可以创建一个专门的单元类并链接到XIB。这听起来对我来说是最干净的方法。这是链接:http://icodeblog.com/2009/05/24/custom-uitableviewcell-using-interface-builder/