前言
随着iOS的更新,大的变动似乎没有,小的变动却很多。而且国内的开发者和国外的有一个很大的不同点,就是国内的求稳,国外的求创新。我接手过的几个项目,最低支持的还有iOS7的,普遍的都是iOS8、9。代码很少会考虑到iOS12,13以上的新特性。2022年4月25号,苹果强制打包的SDK必须升级到iOS15,之前是警告我都没在意,现在成错误了。最开始我还以为可以像添加真机调试包那样不用升级XCode就可以,后来发现我想多了。必须升级XCode13,升级XCode的过程也是一波三折。XCode13.2.1必须要MacOS Big Sur(MacOS 11),我的电脑是12年的mini,然后各种求救,发现可以通过打补丁升级。反正折腾了一天,搞好了之后升级然后发现项目出现各种各样的UIbug,什么导航栏透明变白的,UIBarButtonItem的颜色出现问题啊,TabBar也出现问题。弄好了之后。我就发现了一个问题。苹果的代码风格变了,如果你写过Flutter或者用过SwiftUI,你就越发觉得苹果的代码设计风格变了,特别是在UIKit里,频繁的出现了一个词Configuartion
,像UILabel
,UITabbar
,UIButton
,UINavigationBar
等等。都有与之对应的Configuration对象。甚至像基础类型UIColor
,backgroundColor
都有与之对应的Configuration对象。这样的设计风格初看有些繁琐,但用的多了,反而很顺手,这种设计模式就是23种设计模式中的装饰模式。
UIButtonConfiguration作用的UI元素
UIButtonConfiguration
既然是装饰模式的一种实践,那么它必然只会对UI元素做集成,像消息传递、手势、动作、它是不参与的,颜色文本图片背景样式的,它都可以涉及到。
API详述
弄清楚一个类的成员属性和方法,就是写个案例试一下。这样是最快的,比google和百度都好的多。但也仅限于UI类。像一些抽象类,工具类查文档更适合。
1. 初始化方法
///主要生成扁平纯净的ButtonConfiguration,不设置额外属性
+ (instancetype)plainButtonConfiguration;
///着色类ButtonConfiguration,主要特点是背景色会半透明化,比如设置yellowColor的背景色,而背景的颜色呈现半透明黄色
+ (instancetype)tintedButtonConfiguration;
///半透明灰色背景按钮,设置自定义basebackgroundColor会改变背景色,也就是它的优先级低一点。
+ (instancetype)grayButtonConfiguration;
///填充类ButtonConfiguration,它的效果和plainButtonConfiguration
类似(暂时未发现不同)
+ (instancetype)filledButtonConfiguration;
///强制无边框圆角类ButtonConfiguration,它的优先级最高,如果你设置了btn.layer.cornerRadius, 或者config.background也设置了圆角或者设置了config.cornerStyle都不会出现圆角。
+ (instancetype)borderlessButtonConfiguration;
///内置设置了一个圆角为5左右的ButtonConfiguration,但优先级小于btn.layer.cornerRadius
+ (instancetype)borderedButtonConfiguration;
///圆角半透明背景的ButtonConfiguration
+ (instancetype)borderedTintedButtonConfiguration;
///圆角个性化ButtonConfiguration,主要体现在背景色不透明,文字和图片着色和背景色相反(例如黑色背景,白色文字或者白色背景,黑色文字等)。这一前提在于不主动设置文字颜色和图片颜色的前提。一般情况下,如果不设置文字和图片颜色,它们的颜色则为系统默认的系统蓝色。
+ (instancetype)borderedProminentButtonConfiguration;
///init和new方法是不允许使用的
+ (instancetype)new NS_UNAVAILABLE;
- (instancetype)init NS_UNAVAILABLE;
///这个方法,根据说明是复制指定button的ButtonConfiguration。但有一个疑问,这个是实例方法。我试了一下,它并不能复制指定状态的configuration,比如normal状态是ConfigA,selected状态是configB,但复制只会复制normal状态的值,这和直接btn.configuration = config貌似没有什么区别,可能是我研究不深,没有发现它们的区别
- (instancetype)updatedConfigurationForButton:(UIButton *)button;
2. 属性变量
///这个是一个关于background
相关的config对象,他能处理的内容太多了,背景色,圆角,边框,阴影,自定义绘图(贝塞尔曲线绘图等),毛玻璃特效,背景图片等。只要和背景相关的UI元素,基本都能处理。
@property (nonatomic, readwrite, strong) UIBackgroundConfiguration *background;
///圆角风格枚举
-1,固定圆角,0:动态圆角这两个值其实差不多,就是圆角值可以通过btn.layer.cornerRadius和config.background.cornerRadius两种方式设置。1、2、3这三个值代表内置的小、中、大三个内置的圆角,他的优先级比btn.layer.cornerRadius和config.background.cornerRadius
高,4是胶囊风格,就是圆角切成胶囊状,优先级和1、2、3一样。
@property (nonatomic, readwrite, assign) UIButtonConfigurationCornerStyle cornerStyle;
///内置的title字体大小,优先级比attributedTitle
低,也就是你不想自己设置title和Subtitle字体大小时可以用这个枚举来参考设置,一般来讲如果自己的UI设计和审美不怎么样,或者无所谓时用一用系统推荐的也不错,毕竟苹果的审美你没法说别人不好吧。
@property (nonatomic, readwrite, assign) UIButtonConfigurationSize buttonSize;
///看字面意思就是适配mac端的按钮样式。
@property (nonatomic, readwrite, assign) UIButtonConfigurationMacIdiomStyle macIdiomStyle;
///文本颜色,title和Subtitle,根据文档,它的优先级比attributedTitle
和attributedSubtitle
以及它俩的变形器都低。它是一个全局设置变量,当你想按钮任何状态文本颜色不变时,可以设置这个值
@property (nonatomic, readwrite, strong, nullable) UIColor *baseForegroundColor;
///背景色,同理它的优先级比background
低。
@property (nonatomic, readwrite, strong, nullable) UIColor *baseBackgroundColor;
///图片
@property (nonatomic, readwrite, strong, nullable) UIImage *image;
///图片颜色变形器,他是一个输入颜色,再输出颜色的回调,目前不清楚设置成回调的好处,反正你在回调里直接返回你想要设置的颜色即可
@property (nonatomic, readwrite, copy, nullable) UIConfigurationColorTransformer imageColorTransformer;
///内置的系统矢量图图标config对象,你可以设置指定风格,大小,颜色的矢量图图标作为按钮的image
@property (nonatomic, readwrite, copy, nullable) UIImageSymbolConfiguration *preferredSymbolConfigurationForImage;
///是否显示加载转圈的HUD图标(菊花指示器),HUD的位置和image是一致的,且hud和image不能同时存在
@property (nonatomic, readwrite, assign) BOOL showsActivityIndicator;
///加载HUD指示器的颜色变形器
@property (nonatomic, readwrite, copy, nullable) UIConfigurationColorTransformer activityIndicatorColorTransformer;
///主标题的文本,富文本及变形器
@property (nonatomic, readwrite, copy, nullable) NSString *title; @property (nonatomic, readwrite, copy, nullable) NSAttributedString *attributedTitle; @property (nonatomic, readwrite, copy, nullable) UIConfigurationTextAttributesTransformer titleTextAttributesTransformer;
///副标题的文本,富文本及变形器
@property (nonatomic, readwrite, copy, nullable) NSString *subtitle; @property (nonatomic, readwrite, copy, nullable) NSAttributedString *attributedSubtitle; @property (nonatomic, readwrite, copy, nullable) UIConfigurationTextAttributesTransformer subtitleTextAttributesTransformer;
///内容四边距
@property (nonatomic, readwrite, assign) NSDirectionalEdgeInsets contentInsets;
///恢复默认边距
- (void)setDefaultContentInsets;
///图标放置方位,这是一个非常好用的属性,在这之前,如果你想制作右图左文,上图下文的图标,你得设置imageInset和titleInset,关键还要计算文本的size和图标的size。如果你的按钮通过AutoLayout布局,那么这将计算更加困难,你必须将UIButton子类化,在-layoutSubviews
方法中调整,不然无法获取titleLabel和imageView的正确尺寸。现在有了这个属性再配合imagePadding
一切变得那么美好了,亲爱的(grd)苹果的工程师,为什么不早点拿出来啊?有一点需要说明,这是个option枚举,意思是可以多选,但实际应用,并不能达到效果,比如你想图标在右上角,你将值设置为NSDirectionalRectEdgeTop|NSDirectionalRectEdgeTrailing
实际上按钮还是Y轴居中,右边显示,没有啥效果,目前不明啥原因,我猜测可能需要配合UIButton的对齐属性使用才有效果。
@property (nonatomic, readwrite, assign) NSDirectionalRectEdge imagePlacement;
///图标距文本的距离
@property (nonatomic, readwrite, assign) CGFloat imagePadding;
///标题与子标题的间距,可以为负值
@property (nonatomic, readwrite, assign) CGFloat titlePadding;
///按钮文本对齐风格
@property (nonatomic, readwrite, assign) UIButtonConfigurationTitleAlignment titleAlignment;
///选中状态时是否更新buttonConfigration,实际用不到,下面会体现
@property (nonatomic, readwrite, assign) BOOL automaticallyUpdateForSelection;
///下面是UIButton的属性,这个也是核心,根据按钮不同的状态切换不同的buttonConfiguration,值得注意的是每个分支结尾必须重新赋值,示例如下
btn.configurationUpdateHandler = ^(UIButton *b) {
if (b.state == UIControlStateHighlighted) {
btnConfig.showsActivityIndicator = YES;
btnConfig.attributedTitle = [[NSAttributedString alloc] initWithString:@"Highlighted Title" attributes:@{NSForegroundColorAttributeName:[UIColor systemRedColor]}];
btnConfig.attributedSubtitle = [[NSAttributedString alloc] initWithString:@"Highlighted Subtitle" attributes:@{NSForegroundColorAttributeName:[UIColor systemRedColor]}];
btnConfig.image = [UIImage systemImageNamed:@"square.and.arrow.up.fill"];
btnConfig.imageColorTransformer = ^UIColor * _Nonnull(UIColor * _Nonnull color) {
return [UIColor systemPurpleColor];
};
///这个赋值操作必须写,不然不生效
b.configuration = btnConfig;
}else {
btnConfig.showsActivityIndicator = NO;
btnConfig.attributedTitle = [[NSAttributedString alloc] initWithString:@"Normal Title" attributes:@{NSForegroundColorAttributeName:[UIColor systemRedColor]}];
btnConfig.attributedSubtitle = [[NSAttributedString alloc] initWithString:@"Normal Subtitle" attributes:@{NSForegroundColorAttributeName:[UIColor systemRedColor]}];
btnConfig.image = [UIImage systemImageNamed:@"square.and.arrow.up"];
btnConfig.imageColorTransformer = ^UIColor * _Nonnull(UIColor * _Nonnull color) {
return [UIColor systemOrangeColor];
};
///这个赋值操作必须写,不然不生效
b.configuration = btnConfig;
}
};
还有一个要注意的点
///这行代码生效
btnConfig.baseForegroundColor = [UIColor systemGreenColor];
btn.configuration = btnConfig;
///这行代码不生效
btnConfig.baseForegroundColor = [UIColor systemRedColor];
总结
UIButtonConfiguration的优点在我看来有以下几点
- 使用装饰模式,让责任区分更加明确。
- 更多内置样式,在某些场景使用更方便。
- API扩展性更强大了,这可能是最重要的一点了。给按钮加自定义背景视图,比如毛玻璃,多彩渐变等。给按钮添加加载指示器。调整按钮的图文布局等,在以前的环境的下,要添加大量代码。而现在变得更加简单了。
唯一的缺点是版本要求太高,无法兼容低版本。