Garder un UIButton sélectionné après une pression

90

Une fois que mon utilisateur a cliqué sur un bouton, j'aimerais que ce bouton reste enfoncé pendant le temps que j'effectue une opération réseau. Une fois l'opération réseau terminée, je souhaite que le bouton revienne à son état par défaut.

J'ai essayé d'appeler - [UIButton setSelected:YES]juste après avoir appuyé sur le bouton (avec un appel correspondant à - [UIButton setSelected:NO]après la fin de mon opération réseau), mais cela ne semble rien faire. Même chose si j'appelle setHighlighted:.

Je suppose que je pourrais essayer d'échanger l'image d'arrière-plan pour indiquer un état sélectionné pendant la durée de l'opération réseau, mais cela semble être un piratage. De meilleures suggestions?

Voici à quoi ressemble mon code:

- (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;
}
Greg Maletic
la source

Réponses:

73

Comment définissez-vous les images pour les différents UIControlStatessur le bouton? Définissez-vous une image d'arrière-plan pour UIControlStateHighlightedainsi que UIControlStateSelected?

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

Si vous définissez l'état sélectionné sur l'événement de toucher vers le bas du bouton plutôt que de retoucher à l'intérieur, votre bouton sera en fait dans un état en surbrillance + sélectionné, vous voudrez donc le définir également.

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

Éditer:

Pour résumer mes remarques dans les commentaires et pour adresser le code que vous avez posté ... vous devez définir vos images d'arrière-plan pour l' UIControlétat complet dans lequel vous vous trouvez. Selon votre extrait de code, cet état de contrôle serait désactivé + sélectionné + mis en évidence pendant la durée de l'exploitation du réseau. Cela signifie que vous devrez faire ceci:

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

Si vous supprimez le highlighted = YES, vous en aurez besoin:

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

Obtenez l'image?

Bryan Henry
la source
En fait, je crois que je fais ce que vous suggérez; Je le fais simplement via Interface Builder. J'ai ajouté le code que j'utilise à ma question ci-dessus ... peut-être que cela éclairerait le problème? Le comportement que je souhaite est que je souhaite que le bouton reste sélectionné pendant toute la durée de mon fonctionnement réseau. Ce que je vois, c'est que le bouton est mis en surbrillance lorsqu'il est touché, puis le surlignage disparaît lorsque le toucher est terminé. Le bouton -never- est sélectionné. Pourquoi cela ne marcherait pas n'a aucun sens pour moi, alors j'ai l'impression de faire quelque chose de stupide. J'ai vérifié toutes mes connexions IB et elles sont bonnes ...
Greg Maletic
Je ne crois pas que vous puissiez définir une image d'arrière-plan pour l'état en surbrillance + sélectionné dans Interface Builder, uniquement les états mis en surbrillance et sélectionnés séparément. Si votre bouton était dans un état tel que mis en surbrillance et sélectionné étaient tous les deux définis, je pense qu'il serait par défaut votre image normale, pas l'une des images en surbrillance ou sélectionnées. À partir de votre code, vous définissez l'état du bouton de sorte que la sélection et la surbrillance soient OUI. CheckInButtonPushed est-il connecté à "Touch Up Inside" ou à autre chose?
Bryan Henry
Oui, il est connecté à "Touch Up Inside". Et si je supprime le code lié à l'état «mis en évidence», cela ne fonctionne toujours pas. L'état «sélectionné» n'est jamais affiché.
Greg Maletic
Aussi, pourquoi désactivez-vous le bouton? Cela signifierait en fait que l'état du bouton est désactivé + mis en surbrillance + sélectionné. Si vous avez supprimé le paramètre de ligne mis en surbrillance sur OUI, vous avez désactivé + sélectionné, vous devez donc définir une image pour l'état (UIControlStateDisabled | UIControlStateSelected).
Bryan Henry
D'accord, c'était le problème. Lorsque j'ai retiré la fonctionnalité de désactivation, cela a fonctionné comme prévu. Merci! (Pour répondre à votre question, je désactive le bouton parce que je ne veux pas que quelqu'un le repousse pendant que le réseau est en cours.)
Greg Maletic
30

J'ai un moyen plus simple. Utilisez simplement "performSelector" avec un délai de 0 pour effectuer [button setHighlighted:YES]. Cela effectuera une re-surbrillance après la fin de la boucle d'exécution actuelle.

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

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

"Tout s'améliore lorsque vous allumez l'appareil"

    button.selected = !button.selected;

fonctionne parfaitement ... après avoir connecté la prise au bouton dans l'Interface Builder.

Vous n'avez pas besoin de setBackgroundImage: forState :, le générateur vous permet de spécifier les images d'arrière-plan (redimensionnées si nécessaire) ou / et de premier plan (sans redimensionnement).

18446744073709551615
la source
27

Essayez d'utiliser NSOperationQueue pour y parvenir. Essayez le code comme suit:

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

J'espère que cela t'aides.

roberto.buratti
la source
C'est une excellente solution, Roberto & Parth. Un petit détail, cependant, est que vous verrez parfois un "scintillement" si l'utilisateur maintient son doigt sur le bouton. Existe-t-il un moyen d'éviter le scintillement?
Scott Lieberman
8

Utilisez un bloc pour ne pas avoir à créer une méthode entièrement distincte:

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

Mettre à jour

Pour être clair, vous devez toujours définir l'arrière-plan (ou l'image normale) pour les états de combinaison ainsi que pour les états réguliers comme sbrocket dit dans la réponse acceptée. À un moment donné, votre bouton sera à la fois sélectionné et mis en surbrillance, et vous n'aurez pas d'image pour cela à moins que vous ne fassiez quelque chose comme ceci:

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

Sinon, votre bouton peut revenir à l'image UIControlStateNormal pour le bref état sélectionné + surligné et vous verrez un flash.

Bob Spryn
la source
FYI dispatch_get_current_queue()est déclaré comme étant "peu fiable".
Jacob Relkin le
1
Où est-ce indiqué? Quelle est l'alternative?
Bob Spryn le
Y a-t-il une signification à utiliser dispatch_afterpar opposition à dispatch_async?
Ilya
Oui. dispatch_asyncest un meilleur choix.
Bob Spryn
3

En rapidité, je le fais comme suit.

Je crée une sous-classe UIButtonet implémente une propriété personnaliséestate

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
        }
    }

}

Fonctionne parfaitement pour moi.

Eike
la source
1

J'ai eu un problème similaire où je voulais qu'un bouton garde sa surbrillance après clic. Le problème est que si vous essayez d'utiliser à l' setHighlighted:YESintérieur de vous l'action de clic, elle sera réinitialisée juste après avoir cliqué sur action,- (IBAction)checkInButtonPushed

J'ai résolu cela en utilisant un NSTimer comme celui-ci

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

puis appelez setHighlighted:YESde ma selectCurrentIndexméthode. J'utilise des UIButtonTypeRoundedRectboutons réguliers .

Fredrik
la source
0

J'ai un autre moyen ... si vous ne voulez pas utiliser d'images et que vous voulez l'effet d'un bouton enfoncé, vous pouvez sous-classer le bouton et voici mon code:

dans le fichier .h:

@interface reservasButton : UIButton {

BOOL isPressed;
}
 @end

Dans le fichier .m:

#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;

     }


 } `
Pach
la source
0

Voici une implémentation C # / MonoTouch (Xamarin.iOS) à l'aide des approches présentées ci-dessus. Il suppose que vous avez déjà défini l'état de l'image en surbrillance et configure les états sélectionné et sélectionné | en surbrillance pour utiliser la même 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;
        });
    });
};
t9mike
la source