创建UIButton子类

6

我尝试通过创建UIButton子类来添加一个活动指示器,但是当我使用initWithFrame:(因为我正在子类化UIButton,所以我不使用buttonWithType:)时,按钮不会显示。此外,在这种情况下,我该如何设置按钮类型?

我的视图控制器:

    ActivityIndicatorButton *button = [[ActivityIndicatorButton alloc] initWithFrame:CGRectMake(10, 10, 300, 44)];
    [button addTarget:self action:@selector(buttonPressed) forControlEvents:UIControlEventTouchUpInside];
    [button setTitle:@"Older Posts..." forState: UIControlStateNormal];
    [cell addSubview:button];
    [button release];

我的ActivityIndicatorButton类:

#import <Foundation/Foundation.h>


@interface ActivityIndicatorButton : UIButton {

    UIActivityIndicatorView *_activityView;
}

-(void)startAnimating;
-(void)stopAnimating;
@end

@implementation ActivityIndicatorButton

- (id)initWithFrame:(CGRect)frame {
    if (self=[super initWithFrame:frame]) {
        _activityView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
        _activityView.frame = CGRectOffset(_activityView.frame, 60.0f, 10.0f);

        [self addSubview: _activityView];
    }
    return self;
}

-(void) dealloc{
    [super dealloc];
    [_activityView release];
    _activityView = nil;
}

-(void)startAnimating {
    [_activityView startAnimating];
}

-(void)stopAnimating {
    [_activityView stopAnimating];
}
@end
5个回答

10

偏向组合而非继承。

创建一个包含所需组件的UIView,并将它们添加到你的视图中。


我能理解你的意思,我很喜欢它。但更多的信息会更好。 - To1ne
看看我的帖子,我意识到了这一点。 - Denis Kutlubaev

5

我遇到了类似的情况,并且同意Jeff的看法,你不需要真正地子类化UIButton。我通过子类化UIControl来解决这个问题,然后重写layoutSubviews来配置我想要在我的“按钮”上显示的所有视图。与子类化UIButton相比,这是一个更简单的实现,因为似乎在内部有一些隐藏的魔力。我的实现看起来像这样:

- (id)initWithFrame:(CGRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
    self.opaque = YES;

    self.imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
    [self addSubview:self.imageView];

    self.textLabel = [[UILabel alloc] initWithFrame:CGRectZero];
    [self addSubview:self.textLabel];
    }

return self;
}

而layoutSubviews看起来像这样:

- (void)layoutSubviews {
[super layoutSubviews];

// Get the size of the button
CGRect bounds = self.bounds;

// Configure the subviews of the "button"
...
}

你能否添加一些如何布局textLabel和imageView的示例? - Tim Büthe
在 initWithFrame 中,您可以为 textLabel 和 imageView 分配 CGRectZero 的框架并进行 alloc/init。在此处将它们作为 UIControl 的子视图添加。在 layoutSubviews 中,设置标签和图像视图的框架。 - jmstone617

2

我已经创建了一个自定义类,更喜欢组合而非继承,并且它运作得很完美。我的自定义类有一个按钮,它知道它的MCContact对象。它还使用传递的MCContact对象自动绘制适当的按钮并计算框架。

头文件示例:

#import <UIKit/UIKit.h>

@protocol MCContactViewDelegate;

@interface MCContactView : UIView
{

}

@property (nonatomic, strong) MCContact *mcContact;
@property (nonatomic, weak) id <MCContactViewDelegate> delegate;

- (id)initWithContact:(MCContact*)mcContact delegate:(id <MCContactViewDelegate>)delegate;

@end

@protocol MCContactViewDelegate <NSObject>

- (void)contactViewButtonClicked:(MCContactView*)contactView;

@end

实现文件:

#import "MCContactView.h"

@interface MCContactView()
{
    UIButton *_button;
}

@end

@implementation MCContactView

- (id)initWithContact:(MCContact*)mcContact delegate:(id <MCContactViewDelegate>)delegate
{
    self = [super initWithFrame:CGRectZero];

    if (self) {

        GetTheme();

        _mcContact = mcContact;
        _delegate = delegate;
        _button = [UIButton buttonWithType:UIButtonTypeCustom];

        UIImage *normalBackgroundImage = [[UIImage imageNamed:@"tokenNormal.png"] stretchableImageWithLeftCapWidth:12.5 topCapHeight:12.5];
        [_button setBackgroundImage:normalBackgroundImage forState:UIControlStateNormal];

        UIImage *highlightedBackgroundImage = [[UIImage imageNamed:@"tokenHighlighted.png"] stretchableImageWithLeftCapWidth:12.5 topCapHeight:12.5];
        [_button setBackgroundImage:highlightedBackgroundImage forState:UIControlStateHighlighted];

        _button.titleLabel.font = [theme contactButtonFont];
        [_button setTitleColor:[theme contactButtonTextColor] forState:UIControlStateNormal];

        [_button setTitleEdgeInsets:UIEdgeInsetsMake(4, 6, 4, 6)];

        NSString *tokenString = ([allTrim(mcContact.name) length]>0) ? mcContact.name : mcContact.eMail;
        [_button setTitle:tokenString forState:UIControlStateNormal];

        [_button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];

        CGSize size = [tokenString sizeWithFont:[theme contactButtonFont]];
        size.width += 20;
        if (size.width > 200) {
            size.width = 200;
        }
        size.height = normalBackgroundImage.size.height;
        [_button setFrame:CGRectMake(0, 0, size.width, size.height)];

        self.frame = _button.frame;
        [self addSubview:_button];
    }

    return self;
}


- (void)buttonClicked:(id)sender
{
    [self.delegate contactViewButtonClicked:self];
}

/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    // Drawing code
}
*/

@end 

1
你有一个非常明显的问题,涉及到你的dealloc方法:[super dealloc]; 必须在你的实现的结尾处调用,否则在那之后的代码将尝试访问已经被释放的内存空间(ivar空间),这样会导致程序崩溃。
至于另一个问题,我不确定将活动监视器作为按钮的子视图是否是一个好主意...

不仅会崩溃,而且我遇到的所有这类示例都很难追踪。调试器没有在问题源处中断。 - bandejapaisa

-2

您真的不想去子类化UIButton。它是一个类簇,因此单个实例将是像UIRoundRectButton这样的其他私有的Apple类。您需要子类化做些什么呢?


嗨,Jeff,我正在尝试将活动指示器视图放入按钮中。 - prostock
没问题。只需要将活动指示器视图作为按钮的子视图添加即可。 - Jeff Kelley
68
UIButton并不是一个类簇。类簇由公共抽象类表示,意味着没有实例变量,并有一些私有具体子类提供抽象类的实现。另一方面,UIButton是一个具体类,它的所有方法都不是抽象的,并且它有实例变量来存储通过其参数传递的值。唯一的问题在于+ buttonWithType可以实例化子类而不是直接实例化UIButton本身,因此它可以被视为工厂方法,而不是类簇... - Psycho
Psycho是正确的。我怎么知道?因为我子类化了UIButton来实现自己的按钮(基本上使用IB中的运行时属性来定义按钮的外观)。 - Feloneous Cat
4
补充一下@Psycho的评论,从buttonWithType:的文档来看:该方法是一个方便的构造器,用于创建具有特定配置的按钮对象。如果你继承了UIButton,这个方法不会返回你子类的实例。如果你想创建一个特定子类的实例,你必须直接使用alloc/init创建按钮。 - samvermette

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