【问题标题】:Create a custom UIButton class with delete function创建具有删除功能的自定义 UIButton 类
【发布时间】:2012-04-28 22:48:59
【问题描述】:

我有一个 UIButton 网格。当我点击“编辑”按钮时,我希望在每个按钮上出现一个删除按钮,按下该按钮会删除按钮(和相关数据)。有点像苹果的主屏幕,当你按住一个按钮时,它开始摆动,角落里有一个 X。

根据这篇文章:Subclass UIButton to add a property 我可以使用关联引用为我的每个按钮添加一个属性。我试图添加一个 UIButton 作为我的自定义 UIButton 的属性,但我似乎无法让它出现并且感觉这不是正确的方法。这是我的自定义主按钮:

    #import "UIButton+Property.h"
#import <objc/runtime.h>

@implementation UIButton(Property)

static char UIB_DELETEBUTTON_KEY;

@dynamic deleteButton;


- (void)setDeleteButton:(UIButton *)deleteButton {
    deleteButton = [UIButton buttonWithType:UIButtonTypeInfoDark];
    deleteButton.frame = CGRectMake(100, 100, 50, 50);
    objc_setAssociatedObject(self, &UIB_DELETEBUTTON_KEY, deleteButton, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (UIButton *)deleteButton {
    return (UIButton *)objc_getAssociatedObject(self, &UIB_DELETEBUTTON_KEY);
}

@end

这是我以编程方式添加按钮的地方:

//Create a custom button for each custom book doc
for (int i = 0; i < [customBookDocs count]; ++i) {
    BookDoc *customBookDoc = [customBookDocs objectAtIndex:i];
    NSString *bookTitle = customBookDoc.book.title;

    //create a button for each book
    CGRect frame = CGRectMake(xCoord, yCoord, 200, 200);
    UIButton *bookButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    bookButton.bookDoc = customBookDoc;
    [bookButton setFrame:frame];
    [bookButton setTitle:bookTitle forState:UIControlStateNormal];
    [bookButton addTarget:self action:@selector(bookButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
    xCoord += 250;

    [self.view addSubview:bookButton];
    [self.view addSubview:bookButton.deleteButton];
}

有没有更简单、更明智的方法来做到这一点?还是我走在正确的轨道上?

【问题讨论】:

  • 您从未真正设置过删除按钮。此外,您的关联方法只会覆盖您设置的删除按钮。

标签: ios objective-c iphone xcode uibutton


【解决方案1】:

原始回复开始:

...其他人可能对此有更多话要说,但我不确定您为什么需要在这里使用对象关联。您当然可以使用常规子类化将另一个按钮作为属性添加到您的按钮,这是我将采取的路线。 ...

以下编辑:

我以为我直接继承了一个UI控件,但是我去寻找代码的时候发现我错了。 @Joe 在 cmets 中正确指出直接子类化 UI 控件存在问题。

通过创建一个包装类来保存按钮及其相关的删除按钮,我能够在不使用关联对象的情况下实现类似于您描述的功能。它可以工作,但不是很灵活,所以我一般会推荐@Joe 的方法作为更好的解决方案。

以下是相关代码:

为了简单起见,我将所有代码都放入了 appDelegate。我不建议在现实生活中这样做。

AppDelegate.m:

@implementation AppDelegate

@synthesize window = _window;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

    self.window.backgroundColor = [UIColor whiteColor];

    UIButton *toggleDeleteButtons = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [toggleDeleteButtons setFrame:CGRectMake(20, 45, 280, 45)];
    [toggleDeleteButtons setTitle:@"Toggle Delete" forState:UIControlStateNormal];
    [toggleDeleteButtons addTarget:self action:@selector(toggleDeleteButtonAction) forControlEvents:UIControlEventTouchUpInside];
    [[self window] addSubview:toggleDeleteButtons];

    ButtonWrapper *myButtonWrapper = [[ButtonWrapper alloc] init];
    [[myButtonWrapper button] setFrame:CGRectMake(20, 100, 200, 45)];
    [[myButtonWrapper button] setTitle:@"This is my button" forState:UIControlStateNormal];
    [[myButtonWrapper deleteButton] addTarget:self action:@selector(buttonDeleteRequested:) forControlEvents:UIControlEventTouchUpInside];
    [[myButtonWrapper deleteButton] setTag:0];
    [[self window] addSubview:[myButtonWrapper button]];
    buttonWrapper1 = myButtonWrapper;

    // Added instance called anotherButtonWrapper with tag 1, as above

    // Added instance called stillAnotherButtonWrapper with tag 2, as above

    [self.window makeKeyAndVisible];
    return YES;
}

- (void)toggleDeleteButtonAction {
    static BOOL deleteButtonsShown;

    [buttonWrapper1 showDeleteButton:!deleteButtonsShown];
    [buttonWrapper2 showDeleteButton:!deleteButtonsShown];
    [buttonWrapper3 showDeleteButton:!deleteButtonsShown];
    deleteButtonsShown = !deleteButtonsShown;
}

- (void)buttonDeleteRequested:(UIButton *)deleteButton {
    // delete the specified button here
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Delete" message:[NSString stringWithFormat:@"Delete was pressed on button %i",[deleteButton tag]]delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
}

ButtonWrapper.m:

@implementation ButtonWrapper

@synthesize button;
@synthesize deleteButton;

- (ButtonWrapper *)init {
    ButtonWrapper *newWrapper = [ButtonWrapper alloc];

    UIButton *myButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [myButton setFrame:CGRectZero];

    UIButton *myDeleteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    [myDeleteButton setFrame:CGRectMake(0, 0, 100, 40)];
    [myDeleteButton setTitle:@"Delete" forState:UIControlStateNormal];
    [myDeleteButton setHidden:TRUE];
    [myButton addSubview:myDeleteButton];

    [newWrapper setButton:myButton];
    [newWrapper setDeleteButton:myDeleteButton];

    return newWrapper;
}

- (void)showDeleteButton:(BOOL)showButton {
    if (showButton) {
        [[self deleteButton] setHidden:FALSE];
        [[self deleteButton] setEnabled:TRUE];    }
    else {
        [[self deleteButton] setHidden:TRUE];
        [[self deleteButton] setEnabled:FALSE];
    }
}
@end

这个解决方案不需要我实现所有的 UI 属性,但它确实需要额外的工作来连接嵌入式委托,这很麻烦。可能有一种方法可以在初始化时将委托传递给包装器,但我无法使其工作。

【讨论】:

  • UIButton 是类集群的一部分,因此关联引用更有意义。
  • +1 @Joe。出于这个原因,我倾向于继承 UIControl 而不是 UIButton,因为基本按钮是如此的丑陋,你不会失去太多,也没有太多可以添加来恢复按钮功能。
  • 我已经读过,一般来说,子类化 UIButton 是一个坏主意,并且需要大量代码来复制基本行为。这就是我尝试关联引用的原因,即使我不确定我在那里做什么......但如果我无法弄清楚,我可以回到子类化 UIButton。
  • @Joe,你能扩展你的评论吗?我之前没有遇到过子类化控件的问题,至少就添加属性而言。再说一次,直到最近我才知道关联引用,所以我很想了解更多关于何时适合使用它们的信息。在这种情况下有什么好处(或者我应该问,让 UIButton 将 UIButton 作为传统属性有什么缺点)?免得你误会,我根本不是在和你争论,恰恰相反,只是想学习。
  • @strings42 当您将UIButton 子类化并尝试以除alloc/init 之外的任何其他方式创建它时,您将无法访问任何相关属性。例如[UIButton buttonWithType:UIButtonTypeInfoDark] 不会返回您的子类的实例,因此您将无法使用它。这就是为什么您需要使用具有关联引用的类别。我建议您尝试创建一个新项目和子类UIButton,然后尝试将其与[UIButton buttonWithType:] 一起使用以亲自查看。
猜你喜欢
  • 2012-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 2013-01-15
相关资源
最近更新 更多