当前位置:网站首页>Uibutton status exploration and customization

Uibutton status exploration and customization

2022-07-06 14:20:00 Code&Ocean

original text :UIButton New state solution – Netease cloud music big front end

Control state

As iOS developer , When it comes to controls , I have to mention UIButton, It serves as a iOS The most commonly used control in the system to respond to user click operations , It provides us with quite rich functions and customizability . And our daily work 80% ~ 90% Doing is with UI Dealing with , Handle different states of controls under different operations of users , The simplest , For example, when the user is not logged in , The button is grayed out and cannot be clicked , When the user clicks, a reverse color effect will appear and be fed back to the user, and so on . Definition of common States , The system gave it very early :

typedef NS_OPTIONS(NSUInteger, UIControlState) {
    
 UIControlStateNormal       = 0,
 UIControlStateHighlighted  = 1 << 0,                  // used when UIControl isHighlighted is set
 UIControlStateDisabled     = 1 << 1,
 UIControlStateSelected     = 1 << 2,                  // flag usable by app (see below)
 UIControlStateFocused API_AVAILABLE(ios(9.0)) = 1 << 3, // Applicable only when the screen supports focus
 UIControlStateApplication  = 0x00FF0000,              // additional flags available for application use
 UIControlStateReserved     = 0xFF000000               // flags reserved for internal framework use
};

NS_OPTIONS Enumeration type
We usually preset UIButton Styles in different states , Then directly change the corresponding state bool value , Easy to use .

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
//  The normal state 
[button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
//  Click to highlight 
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[button setBackgroundImage:[UIImage imageNamed:@"btn_highlighted"] forState:UIControlStateHighlighted];
//  Unavailable 
[button setTitleColor:[UIColor grayColor] forState:UIControlStateDisabled];
//  When the user login status changes , Modify attribute values 
if (/*  The user is not logged in  */) {
    
 button.enabled = NO;
} else {
    
 button.enabled = YES;
}

that UIButton Are there only four states available ? In real development , The state of the control may be many , Four kinds must not be enough .

State combination

First of all, we noticed ,UIControlState The definition of is a NS_OPTIONS, instead of NS_ENUM, Three effective bit The pairwise combination should have 8 States . It's just that we can write Demo Test it :

UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setTitle:@"Normal" forState:UIControlStateNormal];
[btn setTitle:@"Selected" forState:UIControlStateSelected];
[btn setTitle:@"Highlighted" forState:UIControlStateHighlighted];
[btn setTitle:@"Highlighted & Disabled" forState:UIControlStateHighlighted | UIControlStateDisabled];
[btn setTitle:@"Disabled" forState:UIControlStateDisabled];
[btn setTitle:@"Selected & Disabled" forState:UIControlStateSelected | UIControlStateDisabled];
[btn setTitle:@"Selected & Highlighted & Disabled" forState:UIControlStateSelected | UIControlStateHighlighted | UIControlStateDisabled];
[btn setTitle:@"Selected & Highlighted" forState:UIControlStateSelected | UIControlStateHighlighted];

Practice has proved ,

  • UIControlStateHighlighted Follow UIControlStateHighlighted | UIControlStateDisabled
  • UIControlStateSelected | UIControlStateHighlighted Follow UIControlStateSelected | UIControlStateHighlighted | UIControlStateDisabled

The effect is the same , Cover each other .
ControlState
In fact, it's easy to understand , because UIControlStateDisabled And UIControlStateHighlighted Semantically, they should not coexist , So there are six available state combinations . in addition , It is found in practice that , When a state is not styled , It will be to Normal The style of the state covers the bottom , So in daily development , We'd better set the corresponding style for all the States used .

Custom state

With the above combination , We can basically cover 90% Daily development of , But what if you need to use more States ?
We are developing Yinjie I encountered the problem of insufficient status when I was on my personal homepage , For a follow button , It has the following different states ( Here's the picture ):

  1. The currently logged in user is not following this user
  2. The currently logged in user is following this user
  3. The currently logged in user has paid attention to this user
  4. The current login user and this user pay attention to each other

 Focus on the State
In this way, there are three states that users can operate , And each operable state has a corresponding highlight style , So we can't just use selected Status to indicate whether you have paid attention . For this need , An easy way to think of is under different data , Modify the style in the same state :

[button setTitle:@" Focus on " forState:UIControlStateNormal];
[button setTitle:@" Followed " forState:UIControlStateSelected];
//  Pay attention to state changes 
button.selected = YES;
if (/*  The other party also paid attention to me  */) {
    
 [button setTitle:@" Pay attention to each other " forState:UIControlStateSelected];
}

The demand is realized , But the use of controls is no longer simple , We can't set all the States during initialization , Then in a data-driven state , State driven style , And add other logic , And this increase is easy to produce Bug.
Is there a better way to define the state , In order to realize the The style is set only once
Look back UIControlState The definition of , There is one UIControlStateApplication It seems that it has never been used , Can it be customized ?
We reuse selected Status as our concern followed state , And add loading Attention status , and mutual Pay attention to each other's state .

enum {
    
 NKControlStateFollowed  = UIControlStateSelected,
 NKControlStateMutual    = 1 << 16 | UIControlStateSelected,
 NKControlStateLoading   = 1 << 17 | UIControlStateDisabled,
};
@interface NKLoadingButton : UIButton
@property (nonatomic, getter=isLoading) BOOL loading;
@property (nonatomic) UIActivityIndicatorView *spinnerView;
@end
@interface NKFollowButton : NKLoadingButton
@property (nonatomic, getter=isMutual) BOOL mutual;
@end

The definition here needs to be explained as follows :
First , Why shift 16 The operation of ?
because UIControlStateApplication The value of is 0x00FF0000, displacement 16 (16 To 23 All are legal values ) Just let the state bit fall within its range .
16 Turn into the system 2 Base number
So customization should start from 16 Start .
secondly ,loading Users should not click to operate , So it's going to or On disabled state ,mutual It must have been followed Of course. ( namely selected), So it's going to or On selected.

Last ,loading State should be reusable elsewhere , Therefore, there is a separate layer in the inheritance relationship NKLoadingButton.
NKLoadingButton The implementation of is relatively simple , It should be noted that , We're going to rewrite -setEnabled: Method let it in loading At the same time, it is in a non clickable state .

@implementation NKLoadingButton
 - (UIControlState)state
{
    
 UIControlState state = [super state];
 
 if (self.isLoading) {
    
 state |= NKControlStateLoading;
 }
 
 return state;
}
- (void)setEnabled:(BOOL)enabled
{
    
 super.enabled = !_loading && enabled;
}
- (void)setLoading:(BOOL)loading
{
    
 if (_loading != loading) {
    
 _loading = loading;
 
 super.enabled = !loading;
 
 if (loading) {
    
 [self.spinnerView startAnimating];
 } else {
    
 [self.spinnerView stopAnimating];
 }
 
 [self setNeedsLayout];
 [self invalidateIntrinsicContentSize];
 }
}
@end

NKFollowButton The implementation is as follows :

@implementation NKFollowButton
- (instancetype)initWithFrame:(CGRect)frame
{
    
 self = [super initWithFrame:frame];
 if (self) {
     
 [self setTitle:@" Focus on " forState:UIControlStateNormal];
 [self setTitle:@" Followed " forState:UIControlStateSelected];
 [self setTitle:@" Followed " forState:UIControlStateSelected | UIControlStateHighlighted];
 [self setTitle:@" Pay attention to each other " forState:NKControlStateMutual];
 [self setTitle:@" Pay attention to each other " forState:NKControlStateMutual | UIControlStateHighlighted];
 [self setTitle:@"" forState:NKControlStateLoading];
 [self setTitle:@"" forState:NKControlStateLoading | UIControlStateSelected];
 [self setTitle:@"" forState:NKControlStateMutual | NKControlStateLoading];
 
 //  Color related settings are omitted below 
 }
 return self;
}
- (UIControlState)state
{
    
 UIControlState state = [super state];
 
 if (self.isMutual) {
    
 state |= NKControlStateMutual;
 }
 
 return state;
}
- (void)setSelected:(BOOL)selected
{
    
 super.selected = selected;
 if (!selected) {
    
 self.mutual = NO;
 }
}
- (void)setMutual:(BOOL)mutual
{
    
 if (_mutual != mutual) {
    
 _mutual = mutual;
 
 if (mutual) {
    
 self.selected = YES;
 }
 
 [self setNeedsLayout];
 [self invalidateIntrinsicContentSize];
 }
}
@end

We need to rewrite -state Methods let the outside world get complete 、 The right value , rewrite -setSelected: Methods and -setMutual: Method , Let them be mutually exclusive under certain conditions , Unified under certain conditions .
such , We achieved only in -init Set a style in , The subsequent modification is only based on the data returned by the server .selected``.loading.mutual The value of the can !

summary

This article starts from a single state , To the combined state , To customize the status of layers of in-depth introduction UIButton The application of the state of in daily development , Only state drives UI It has always been a good idea in programmer development , This article gives the implementation reference from a basic control . in addition , We are looking at some systems that provide API when , Be sure to think more about what Apple's intention is ? How they want us to use , And how to use it correctly ?

原网站

版权声明
本文为[Code&Ocean]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202131404555616.html