Mantener un UIButton seleccionado después de un toque

90

Después de que mi usuario hace clic en un botón, me gustaría que ese botón permaneciera pulsado durante el tiempo que realizo una operación de red. Cuando se complete la operación de red, quiero que el botón vuelva a su estado predeterminado.

Intenté llamar, [UIButton setSelected:YES]justo después de presionar el botón (con una llamada correspondiente a, [UIButton setSelected:NO]después de que finalice mi operación de red), pero no parece hacer nada. Lo mismo si llamo setHighlighted:.

Supongo que podría intentar cambiar la imagen de fondo para indicar un estado seleccionado durante la operación de red, pero eso parece un truco. ¿Alguna sugerencia mejor?

Así es como se ve mi código:

- (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
fuente

Respuestas:

73

¿Cómo está configurando las imágenes para los diferentes UIControlStatesen el botón? ¿Está configurando una imagen de fondo para UIControlStateHighlightedasí como UIControlStateSelected?

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

Si está configurando el estado seleccionado en el evento de toque hacia abajo del botón en lugar de retocar el interior, su botón estará en un estado resaltado + seleccionado, por lo que también querrá configurarlo.

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

Editar:

Para resumir mis comentarios en los comentarios y abordar el código que publicó ... debe configurar sus imágenes de fondo para el UIControlestado completo en el que se encuentra. De acuerdo con su fragmento de código, este estado de control estaría deshabilitado + seleccionado + resaltado durante la operación de la red. Esto significa que necesitaría hacer esto:

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

Si elimina el highlighted = YES, necesitará esto:

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

¿Obtener la imagen?

Bryan Henry
fuente
En efecto, creo que estoy haciendo lo que sugieres; Solo lo estoy haciendo a través de Interface Builder. Agregué el código que estoy usando a mi pregunta anterior ... ¿quizás eso arrojaría algo de luz sobre el problema? El comportamiento que quiero es que quiero que el botón permanezca seleccionado mientras dure la operación de mi red. Lo que veo es que el botón se resalta cuando se toca, luego el resaltado desaparece cuando se hace el toque. El botón -nunca- se selecciona. Por qué esto no funcionaría no tiene sentido para mí, entonces siento que estoy haciendo algo estúpido.
Revisé
No creo que pueda establecer una imagen de fondo para el estado resaltado + seleccionado en Interface Builder, solo los estados resaltado y seleccionado por separado. Si su botón estaba en un estado tal que resaltado y seleccionado estaban configurados, creo que sería predeterminado en su imagen normal, no en las imágenes resaltadas o seleccionadas. Desde su código, está configurando el estado del botón de modo que tanto el seleccionado como el resaltado sean SÍ. ¿CheckInButtonPushed está conectado a "Touch Up Inside" o algo más?
Bryan Henry
Sí, está conectado a "Touch Up Inside". Y si elimino el código relacionado con el estado 'resaltado', todavía no funciona. El estado 'seleccionado' nunca se muestra.
Greg Maletic
Además, ¿por qué está deshabilitando el botón? Eso en realidad significaría que el estado del botón está deshabilitado + resaltado + seleccionado. Si eliminó la configuración de línea resaltada en YES, entonces ha deshabilitado + seleccionado, por lo que debe configurar una imagen para el estado (UIControlStateDisabled | UIControlStateSelected).
Bryan Henry
De acuerdo, ese era el problema. Cuando saqué la funcionalidad de desactivación, funcionó como se esperaba. ¡Gracias! (Para responder a su pregunta, estoy deshabilitando el botón porque no quiero que nadie lo vuelva a presionar mientras la red está en funcionamiento)
Greg Maletic
30

Tengo una forma más fácil. Simplemente use "performSelector" con 0 retraso para realizar [button setHighlighted:YES]. Esto volverá a resaltar después de que finalice el ciclo de ejecución actual.

- (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
fuente
28

"Todo mejora cuando enciendes"

    button.selected = !button.selected;

funciona perfectamente ... después de conectar la salida al botón en Interface Builder.

No necesita setBackgroundImage: forState :, el constructor le permite especificar el fondo (cambia de tamaño si es necesario) o / y el primer plano (no cambia el tamaño) de las imágenes.

18446744073709551615
fuente
27

Intente usar NSOperationQueue para lograr esto. Pruebe el código de la siguiente manera:

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

Espero que esto ayude.

roberto.buratti
fuente
Esta es una gran solución, Roberto & Parth. Sin embargo, un pequeño detalle es que ocasionalmente verá un "parpadeo" si el usuario mantiene el dedo sobre el botón. ¿Hay alguna forma de evitar el parpadeo?
Scott Lieberman
8

Use un bloque para no tener que crear un método completamente separado:

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

Actualizar

Para que quede claro, aún debe establecer el fondo (o la imagen normal) para los estados de combinación, así como los normales, como dice sbrocket en la respuesta aceptada. En algún momento, su botón estará seleccionado y resaltado, y no tendrá una imagen para eso a menos que haga algo como esto:

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

De lo contrario, su botón puede volver a la imagen UIControlStateNormal para el breve estado seleccionado + resaltado y verá un destello.

Bob Spryn
fuente
FYI dispatch_get_current_queue()se declara como "no confiable".
Jacob Relkin
1
¿Dónde se dice eso? Cual es la alternativa?
Bob Spryn
¿Tiene importancia el uso dispatch_afteren lugar de dispatch_async?
Ilya
Sí. dispatch_asynces una mejor opción.
Bob Spryn
4

Rápidamente lo estoy haciendo de la siguiente manera.

Creo una subclase de UIButtone implementé una propiedad personalizadastate

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

}

Funciona perfectamente para mi.

Eike
fuente
1

Tuve un problema similar en el que quería que un botón mantuviera su resaltado después de hacer clic. El problema es que si intenta utilizar setHighlighted:YESdentro de su acción de clic, se restablecerá inmediatamente después de hacer clic en la acción.- (IBAction)checkInButtonPushed

Resolví esto usando un NSTimer como este

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

y luego llamar setHighlighted:YESdesde mi selectCurrentIndexmétodo. Utilizo UIButtonTypeRoundedRectbotones normales .

Fredrik
fuente
0

Tengo otra forma ... si no desea usar imágenes y desea el efecto de un botón presionado, puede subclasificar el Botón y aquí está mi código:

en el archivo .h:

@interface reservasButton : UIButton {

BOOL isPressed;
}
 @end

En el archivo .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
fuente
0

Aquí hay una implementación de C # / MonoTouch (Xamarin.iOS) usando los enfoques presentados anteriormente. Asume que ya ha establecido el estado de la imagen resaltada y configura los estados seleccionados y seleccionados | resaltados para usar la misma imagen.

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
fuente