触摸后保持选中的 UIButton

在我的用户点击一个按钮后,我希望该按钮在我执行网络操作期间保持按下状态。当网络操作完成后,我希望按钮返回到默认状态。

我试过在按下按钮后立即调用-[UIButton setSelected:YES](在我的网络操作结束后调用-[UIButton setSelected:NO]) ,但它似乎没有任何作用。如果我调用 setHighlighted:也是一样的。

我想我可以尝试交换出的背景图像,以表示一个选定的状态的持续时间的网络操作,但这似乎是一个黑客。有更好的建议吗?

我的代码是这样的:

- (IBAction)checkInButtonPushed
{
self.checkInButton.enabled = NO;
self.checkInButton.selected = YES;
self.checkInButton.highlighted = YES;
[self.checkInActivityIndicatorView startAnimating];
[CheckInOperation startWithPlace:self.place delegate:self];
}


- (void)checkInCompletedWithNewFeedItem:(FeedItem*)newFeedItem wasNewPlace:(BOOL)newPlace possibleError:(NSError*)error;
{
[self.checkInActivityIndicatorView stopAnimating];
self.checkInButton.enabled = YES;
self.checkInButton.selected = NO;
self.checkInButton.highlighted = NO;
}
93976 次浏览

How are you setting the images for the different UIControlStates on the button? Are you setting a background image for UIControlStateHighlighted as well as UIControlStateSelected?

UIImage *someImage = [UIImage imageNamed:@"SomeResource.png"];
[button setBackgroundImage:someImage forState:UIControlStateHighlighted];
[button setBackgroundImage:someImage forState:UIControlStateSelected];

If you're setting the selected state on the button touch down event rather than touch up inside, your button will actually be in a highlighted+selected state, so you'll want to set that too.

[button setBackgroundImage:someImage forState:(UIControlStateHighlighted|UIControlStateSelected)];

Edit:

To sum up my remarks in the comments and to address the code you posted...you need to set your background images for the full UIControl state that you're in. According to your code snippet, this control state would be disabled + selected + highlighted for the duration of the network operation. This means that you would need to do this:

[button setBackgroundImage:someImage forState:(UIControlStateDisabled|UIControlStateHighlighted|UIControlStateSelected)];

If you remove the highlighted = YES, then you would need this:

[button setBackgroundImage:someImage forState:(UIControlStateDisabled|UIControlStateSelected)];

Get the picture?

I had a similar problem where I wanted a button to keep it's highlight after click. The problem is if you try to use setHighlighted:YES inside of you click action it will reset right after you click action, - (IBAction)checkInButtonPushed

I solved this by using a NSTimer like this

NSTimer *timer;
timer = [NSTimer scheduledTimerWithTimeInterval: 0.01
target: self
selector: @selector(selectCurrentIndex)
userInfo: nil
repeats: NO];

and then call setHighlighted:YES from my selectCurrentIndex method. I use regular UIButtonTypeRoundedRect buttons.

I have an easier way. Just use "performSelector" with 0 delay to perform [button setHighlighted:YES] . This will perform re-highlighting after the current runloop ends.

- (IBAction)buttonSelected:(UIButton*)sender {
NSLog(@"selected %@",sender.titleLabel.text);
[self performSelector:@selector(doHighlight:) withObject:sender afterDelay:0];
}


- (void)doHighlight:(UIButton*)b {
[b setHighlighted:YES];
}

"Everything gets better when you turn power on"

    button.selected = !button.selected;

works perfectly... after I connected the outlet to the button in the Interface Builder.

You do not need to setBackgroundImage:forState:, the builder allows you to specify the background (gets resized if necessary) or/and foreground (not resizing) images.

I have another way ...if you don't want to use images, and you want the effect of a pressed button, You can subclass the Button and here's my code:

in the .h File:

@interface reservasButton : UIButton {


BOOL isPressed;
}
@end

In the .m File:

#import <QuartzCore/QuartzCore.h>




@implementation reservasButton


-(void)setupView {  //This is for Shadow


self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOpacity = 0.5;
self.layer.shadowRadius = 1;
self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f); //comment
//   self.layer.borderWidth = 1;
self.contentVerticalAlignment   = UIControlContentVerticalAlignmentCenter;
self.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;


// [self setBackgroundColor:[UIColor whiteColor]];


//  self.opaque = YES;




}


-(id)initWithFrame:(CGRect)frame{
if((self = [super initWithFrame:frame])){
[self setupView];
}


return self;
}


-(id)initWithCoder:(NSCoder *)aDecoder{
if((self = [super initWithCoder:aDecoder])){
[self setupView];
}


return self;
}


//Here is the important code


-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{




if (isPressed == FALSE) {


self.contentEdgeInsets = UIEdgeInsetsMake(1.0,1.0,-1.0,-1.0);
self.layer.shadowOffset = CGSizeMake(1.0f, 1.0f);
self.layer.shadowOpacity = 0.8;


[super touchesBegan:touches withEvent:event];


isPressed = TRUE;


}
else {


self.contentEdgeInsets = UIEdgeInsetsMake(0.0,0.0,0.0,0.0);
self.layer.shadowOffset = CGSizeMake(2.0f, 2.0f);
self.layer.shadowOpacity = 0.5;


[super touchesEnded:touches withEvent:event];


isPressed = FALSE;


}




} `

Try using NSOperationQueue to achieve this. Try out code as follows:

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
theButton.highlighted = YES;
}];

Hope this helps.

Use a block so you don't have to build a whole separate method:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0), dispatch_get_main_queue(), ^{
theButton.highlighted = YES;
});

Update

To be clear you still need to set the background (or normal image) for the combination states as well as the regular ones like sbrocket says in the accepted answer. At some point your button will be both selected and highlighted, and you won't have an image for that unless you do something like this:

[button setBackgroundImage:someImage forState (UIControlStateHighlighted|UIControlStateSelected)];

Otherwise your button can fall back to the UIControlStateNormal image for the brief selected+highlighted state and you'll see a flash.

Here is a C# / MonoTouch (Xamarin.iOS) implementation using approaches presented above. It assumes you have set the Highlighted image state already, and configures the selected and selected|highlighted states to use the same image.

var selected = button.BackgroundImageForState(UIControlState.Highlighted);
button.SetBackgroundImage(selected, UIControlState.Selected);
button.SetBackgroundImage(selected, UIControlState.Selected | UIControlState.Highlighted);
button.TouchUpInside += delegate
{
NSTimer.CreateScheduledTimer(TimeSpan.FromMilliseconds(0), delegate
{
button.Highlighted = true;
NSTimer.CreateScheduledTimer(TimeSpan.FromMilliseconds(200), delegate
{
button.Highlighted = false;
});
});
};

In swift I'm doing it like the following.

I create a Subclass of UIButton and implemented a custom property state

class ActiveButton: UIButton {


private var _active = false
var active:Bool {
set{
_active = newValue
updateState()
}
get{
return _active
}
}


required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.addTarget(self, action: #selector(ActiveButton.touchUpInside(_:)), forControlEvents: .TouchUpInside)
}


func touchUpInside(sender:UIButton) {
active = !active
}


private func updateState() {
NSOperationQueue.mainQueue().addOperationWithBlock {
self.highlighted = self.active
}
}


}

Works perfectly for me.