Сохранение UIButton выбранным после касания

90

После того, как мой пользователь нажимает кнопку, я бы хотел, чтобы эта кнопка оставалась нажатой во время выполнения сетевой операции. Когда сетевая операция будет завершена, я хочу, чтобы кнопка вернулась в состояние по умолчанию.

Я пробовал позвонить - [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;
}
Грег Малетик
источник

Ответы:

73

Как вы устанавливаете изображения для разных UIControlStatesкнопок? Настраиваете вы фоновое изображение для UIControlStateHighlightedкак UIControlStateSelected?

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

Если вы устанавливаете выбранное состояние для события касания кнопки, а не касания внутри, ваша кнопка фактически будет в выделенном + выбранном состоянии, поэтому вы также захотите установить это.

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

Редактировать:

Подводя итог моим замечаниям в комментариях и обращаясь к опубликованному вами коду ... вам необходимо установить фоновые изображения для полного UIControlсостояния, в котором вы находитесь. Согласно фрагменту кода, это состояние управления будет отключено + выбрано + выделяется на время работы сети. Это означает, что вам нужно будет сделать следующее:

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

Если вы удалите highlighted = YES, вам понадобится следующее:

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

Получите картину?

Брайан Генри
источник
По сути, я считаю, что делаю то, что вы предлагаете; Я просто делаю это через Interface Builder. Я добавил код, который использую, в свой вопрос выше ... возможно, это проливает свет на проблему? Я хочу, чтобы кнопка оставалась выбранной на протяжении всей работы в сети. Я вижу, что кнопка подсвечивается при прикосновении, затем подсветка исчезает, когда касание завершено. Кнопка «Никогда» не выбрана. Для меня нет никакого смысла, почему это не сработает, поэтому я чувствую, что делаю что-то глупое. Я проверил все свои связи с IB, и они в порядке ...
Грег Малетик,
Я не верю, что вы можете установить фоновое изображение для выделенного + выбранного состояния в Interface Builder, только выделенные и выбранные состояния отдельно. Если ваша кнопка находилась в таком состоянии, что и выделенный, и выбранный были установлены, я считаю, что по умолчанию будет использоваться ваше обычное изображение, а не выделенные или выбранные изображения. Из вашего кода вы устанавливаете состояние кнопки так, чтобы и выбранный, и выделенный были ДА. CheckInButtonPushing связан с "Touch Up Inside" или чем-то еще?
Брайан Генри
Да, это связано с "Touch Up Inside". И если я удалю код, связанный с «выделенным» состоянием, он все равно не сработает. Состояние «выбрано» никогда не отображается.
Грег Малетик
Кроме того, почему вы отключаете кнопку? На самом деле это будет означать, что состояние кнопки отключено + выделено + выделено. Если вы удалили настройку строки, выделенную на YES, значит, вы отключили + выбрано, поэтому вам нужно установить изображение для состояния (UIControlStateDisabled | UIControlStateSelected).
Брайан Генри
Ладно, вот в чем проблема. Когда я отключил функцию отключения, все заработало, как ожидалось. Благодарность! (Чтобы ответить на ваш вопрос, я отключаю эту кнопку, потому что я не хочу, чтобы кто-нибудь снова
нажимал на нее,
30

У меня есть способ попроще. Просто используйте "performSelector" с нулевой задержкой для выполнения [button setHighlighted:YES]. Это выполнит повторное выделение после завершения текущего цикла выполнения.

- (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
источник
28

«Все становится лучше, когда включаешь питание»

    button.selected = !button.selected;

работает отлично ... после того, как я подключил розетку к кнопке в Интерфейсном Разработчике.

Вам не нужно устанавливать BackgroundImage: forState :, построитель позволяет вам указать фоновые изображения (при необходимости размер изменяется) и / или изображения переднего плана (без изменения размера).

18446744073709551615
источник
27

Для этого попробуйте использовать NSOperationQueue. Попробуйте следующий код:

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

Надеюсь это поможет.

Роберто Буратти
источник
Это отличное решение, Роберто и Парт. Однако есть одна небольшая деталь: вы иногда будете видеть «мерцание», если пользователь удерживает палец на кнопке. Есть ли способ предотвратить мерцание?
Скотт Либерман
8

Используйте блок, чтобы не создавать целый отдельный метод:

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

Обновить

Чтобы было понятно, вам все равно нужно установить фон (или нормальное изображение) для состояний комбинации, а также для обычных, таких как sbrocket, говорит в принятом ответе. В какой-то момент ваша кнопка будет выделена и выделена, и у вас не будет изображения для этого, если вы не сделаете что-то вроде этого:

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

В противном случае ваша кнопка может вернуться к изображению UIControlStateNormal для краткого выбранного + выделенного состояния, и вы увидите вспышку.

Боб Сприн
источник
FYI dispatch_get_current_queue()считается «ненадежным».
Джейкоб Релкин
1
Где это сказано? Какая альтернатива?
Боб Сприн
Есть ли значение в использовании, dispatch_afterа не в dispatch_async?
Илья
Ага. dispatch_asyncлучший выбор.
Боб Сприн
4

Я делаю это так быстро.

Я создал подкласс UIButtonи реализовал настраиваемое свойство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
        }
    }

}

У меня отлично работает.

Эйке
источник
1

У меня была аналогичная проблема, когда я хотел, чтобы кнопка оставалась выделенной после нажатия. Проблема в том, что если вы попытаетесь использовать действие setHighlighted:YESвнутри себя, оно будет сброшено сразу после того, как вы нажмете действие,- (IBAction)checkInButtonPushed

Я решил это, используя такой NSTimer

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

а затем вызовите setHighlighted:YESиз моего selectCurrentIndexметода. Пользуюсь штатными UIButtonTypeRoundedRectкнопками.

Фредрик
источник
0

У меня есть другой способ ... если вы не хотите использовать изображения и хотите получить эффект нажатой кнопки, вы можете создать подкласс Button и вот мой код:

в файле .h:

@interface reservasButton : UIButton {

BOOL isPressed;
}
 @end

В файле .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

Вот реализация C # / MonoTouch (Xamarin.iOS) с использованием подходов, представленных выше. Предполагается, что вы уже установили состояние «Выделенное изображение», и настраивает выбранные и выделенные | выделенные состояния для использования одного и того же изображения.

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
источник