Могу ли я использовать пользовательские значения UIControlState для своего собственного элемента управления? - PullRequest
28 голосов
/ 18 мая 2010

Есть ли способ установить пользовательские состояния - не одно из существующих значений UIControlState - для UIControl?

В перечислении UIControlSate имеется 16 битов, которые можно использовать для пользовательских состояний управления:

UIControlStateApplication  = 0x00FF0000,              // additional flags available for application use

Проблема в том, что свойство UIControl state имеет значение только для чтения .

Я хочу установить различные фоновые изображения для моего UIButton для пользовательских состояний.

Ответы [ 4 ]

36 голосов
/ 18 января 2011

Вы можете использовать пользовательские состояния в подклассе UIControl.

  • Создайте переменную с именем customState, в которой вы будете управлять своими пользовательскими состояниями.
  • Если вам нужно установить состояние, выполните операции с флагом для этой переменной и вызовите [self stateWasUpdated].
  • Переопределите свойство state, чтобы получить [super state] побитовое ИЛИ против вашего customState
  • Переопределите сеттеры enabled, selected и highlighted, чтобы они вызывали [self stateWasUpdated]. Это позволит вам реагировать на любые изменения в состоянии, а не только на customState
  • Реализация stateWasUpdated с логикой для реагирования на изменения в состоянии

В шапке:

#define kUIControlStateCustomState (1 << 16)

@interface MyControl : UIControl {
    UIControlState customState;
}

В реализации:

@implementation MyControl

-(void)setCustomState {
    customState |= kUIControlStateCustomState;
    [self stateWasUpdated];
}

-(void)unsetCustomState {
    customState &= ~kUIControlStateCustomState;
    [self stateWasUpdated];
}

- (UIControlState)state {
    return [super state] | customState;
}

- (void)setSelected:(BOOL)newSelected {
    [super setSelected:newSelected];
    [self stateWasUpdated];
}

- (void)setHighlighted:(BOOL)newHighlighted {
    [super setHighlighted:newHighlighted];
    [self stateWasUpdated];
}

- (void)setEnabled:(BOOL)newEnabled {
    [super setEnabled:newEnabled];
    [self stateWasUpdated];
}

- (void)stateWasUpdated {
    // Add your custom code here to respond to the change in state
}

@end
5 голосов
/ 15 июля 2014

На основе ответа @Nick я реализовал более простую версию. Этот подкласс предоставляет свойство BOOL outlined, которое по функции похоже на selected, highlighted и enabled.

Выполнение таких операций, как [customButtton setImage:[UIImage imageNamed:@"MyOutlinedButton.png"] forState:UIControlStateOutlined], позволяет автоматически работать при обновлении свойства outlined.

Дополнительные из них состояние + свойство могут быть добавлены при необходимости.


UICustomButton.h

extern const UIControlState UIControlStateOutlined;

@interface UICustomButton : UIButton
@property (nonatomic) BOOL outlined;
@end

UICustomButton.m

const UIControlState UIControlStateOutlined = (1 << 16);

@interface OEButton ()
@property UIControlState customState;
@end

@implementation OEButton

- (void)setOutlined:(BOOL)outlined
{
    if (outlined)
    {
        self.customState |= UIControlStateOutlined;
    }
    else
    {
        self.customState &= ~UIControlStateOutlined;
    }
    [self stateWasUpdated];
}

- (BOOL)outlined
{
    return ( self.customState & UIControlStateOutlined ) == UIControlStateOutlined;
}

- (UIControlState)state {
    return [super state] | self.customState;
}

- (void)stateWasUpdated
{
    [self setNeedsLayout];
}

// These are only needed if you have additional code on -(void)stateWasUpdated
// - (void)setSelected:(BOOL)newSelected
// {
//     [super setSelected:newSelected];
//     [self stateWasUpdated];
// }
//
// - (void)setHighlighted:(BOOL)newHighlighted
// {
//     [super setHighlighted:newHighlighted];
//     [self stateWasUpdated];
// }
//
// - (void)setEnabled:(BOOL)newEnabled
// {
//     [super setEnabled:newEnabled];
//     [self stateWasUpdated];
// }

@end
2 голосов
/ 13 августа 2018

Я бы хотел немного улучшить эту стратегию. Посмотрите этот вопрос stackoverflow:

Переопределение isHighlighted все еще изменяет UIControlState - почему?

Оказывается, что реализация Apple state на самом деле является вычисляемым свойством, основанным на других свойствах, isSelected, isHighlighted, isEnabled и т. Д.

Так что на самом деле нет необходимости в настраиваемой битовой маске состояния поверх UIControlState (ну, это не так, что в этом нет необходимости, просто добавляется сложность там, где ее нет / не должно быть).

Если вы хотите быть совместимым с реализацией Apple, вам просто нужно переопределить свойство состояния и проверить свои пользовательские состояния в геттере.

extension UIControlState {
     static let myState = UIControlState(rawValue: 1 << 16)
} 

class MyControl: UIControl {

      override var state: UIControlState {
          var state = super.state
          if self.isMyCustomState {
               state.insert(UIControlState.myState)
          }
          return state
      }

      var isMyCustomState: Bool = false
 }

Это действительно умный путь; Согласно приведенной выше ссылке, если вы переопределите свойство и не измените состояние, вы получите противоречивые результаты. Создание состояния всегда вычисляемым свойством обеспечивает согласованность между свойствами, которые представляет state.

2 голосов
/ 12 июня 2017

Swift 3 версия ответа Ника:

extension UIControlState {
    static let myState = UIControlState(rawValue: 1 << 16)
}

class CustomControl: UIControl {

    private var _customState: UInt = 0

    override var state: UIControlState {
       return UIControlState(rawValue: super.state.rawValue | self._customState)
    }

    var isMyCustomState: Bool {
        get { 
            return self._customState & UIControlState.myState.rawValue == UIControlState.myState.rawValue 
        } set {
            if newValue == true {
                self._customState |= UIControlState.myState.rawValue
            } else {
                self._customState &= ~UIControlState.myState.rawValue
            }
        }
    }
}
...