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

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?


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

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.


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.


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.


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.


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 .


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;

     }


 } `

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;
        });
    });
};
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.