iOS - 创建可以在 InterfaceBuilder 中实时预览的自定义控件
一、需求实现一个前后带图标的输入框
这是一个简单的自定义控件,很容易想到自定义一个视图(UIView),然后前后的图标使用 UIImageView 或者 UIButton 显示,中间放一个 UITextField 就可以了
实现方式上可以 InterfaceBuilder 创建,也可以使用纯代码实现,这个可以根据自己喜好及项目确定
二、实现 InterfaceBuilder 中实时预览
要实现上面的一个需求,相信对大多数人来说并不难,本文主要讲解怎么让自定义控件支持纯代码创建,并且也支持在 InterfaceBuilder 中实时预览,以及本人在编写过程中遇到问题
想要让自定义的控件支持在 InterfaceBuilder 中实时预览,其实也并不难,需要用到两个关键字 IB_DESIGNABLE 和 IBInspectable
IB_DESIGNABLE:用来指示该类可以在 InterfaceBuilder ,在 InterfaceBuilder 中,添加一个 UIView 后,指定 Custom Class 中的 Class 为该类就可以了;这个关键字可以下载 .h 或者 .m 中 @interface 前面即可
代码如下:
IB_DESIGNABLE
@interface ImageTextField : UIView
IBInspectable:用来指示属性在 InterfaceBuilder 中显示属性,可以实时设置;这个关键字只用添加到属性类型前面即可
代码如下:
@property (nonatomic, strong) IBInspectable NSString *text;
这样准备工作,接下来就是在具体实现过程
三、实现及注意事项
1. 纯代码初始化
对于纯代码创建,我们要有一个初始化方法,init 或者 initWithFrame 或者自定义的初始化方法,我们只需要把创建添加子控件在初始化方法中一步一步添加即可,代码约束可以添加到 layoutSubviews
#pragma mark --- 支持代码创建
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setViewsWithFrame:frame];
} return self;
}
- (void)layoutSubviews {
[super layoutSubviews]; // 约束在这里添加,才能保证约束正确执行
[self setConstraints];
}
必须要重写初始化方法:- (instancetype)initWithFrame:(CGRect)frame 我们在使用 InterfaceBuilder 的时候,会调用该方法,所以修改该方法调用的代码都会实时显示到 InterfaceBuilder 中,如果是我们自定义的初始化方法,则不会在修改代码时实时显示,我们为了能够实时预览到修改效果,需要重写系统的初始化方法,同时为了支持纯代码创建实例,
可以把初始化要实现的代码封装起来,这样的话可以方便的在自定义以及重写的初始化方法中方便调用。
那么怎么才能让我们的修改实时显示在,我们需要实现另外一个方法:prepareForInterfaceBuilder 从方法名称就可以猜测到,写在该方法中的代码会反映的到 InterfaceBuilder 这样就能看到实时修改后的效果
示例代码:
#pragma mark --- 实时更新到 InterfaceBuilder
- (void)prepareForInterfaceBuilder {
[self setConstraints];
}
为什么在这里调用?如果在 init 中调用,会导致在 InterfaceBuilder 设置的属性不起作用,那么就看不到真实的效果。这里的代码只是在设计的时候会调用,真实的设置约束的代码是在 layoutSubviews 中调用
2. 不使用纯代码创建
我们只需要在 InterfaceBuilder 中添加一个 UIView 中,然后设置 Class 为自定义的控件就可以了,但是可视化添加的控件,怎么初始化呢?就需要重写下面这个方法来保证正确的运行,在程序运行时,系统会自动调用这个方法创建实例
#pragma mark --- 支持XIB创建
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
// 这里 Frame 可以任意指定,约束会调整视图大小
[self setViewsWithFrame:CGRectZero];
} return self;
}
完整代码如下
ImageTextField.h:
//
// ImageTextField.h
// EXOTerra
//
// 带有图片提示的输入框
//
// Created by huang zhengguo on 2020/1/2.
// Copyright © 2020 Inledco. All rights reserved.
// #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN IB_DESIGNABLE
@interface ImageTextField : UIView // 文字
@property (nonatomic, strong) IBInspectable NSString *text;
// 前面图像
@property (nonatomic, strong) IBInspectable UIImage *image;
// 字体颜色
@property (nonatomic, strong) IBInspectable UIColor *textColor;
// 占位符
@property (nonatomic, strong) IBInspectable NSString *placeholder;
// 占位符颜色
@property (nonatomic, strong) IBInspectable UIColor *placeholderColor;
// 是否隐藏输入
@property (nonatomic, assign) IBInspectable BOOL secureTextEntry;
// 小图标
@property (nonatomic, strong) IBInspectable UIImage *iconImage;
// 是否显示小图标
@property (nonatomic, assign) IBInspectable BOOL isIconVisible;
// 小图标点击回调
@property (nonatomic, copy) void (^iconButtonClickCallback) (ImageTextField *, UIButton *, BOOL); @end NS_ASSUME_NONNULL_END
ImageTextField.m
//
// ImageTextField.m
// EXOTerra
//
// Created by huang zhengguo on 2020/1/2.
// Copyright © 2020 Inledco. All rights reserved.
// #import "ImageTextField.h" @interface ImageTextField() // 前面图标
@property (nonatomic, strong) UIImageView *headerImageView;
// 输入框
@property (nonatomic, strong) UITextField *textField;
// 后面图标
@property (nonatomic, strong) UIButton *iconButton; @end @implementation ImageTextField #pragma mark --- 支持代码创建
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self setViewsWithFrame:frame];
} return self;
} #pragma mark --- 支持XIB创建
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
// 这里 Frame 可以任意指定,在 InterfaceBuilder 中会重新设定
[self setViewsWithFrame:CGRectZero];
} return self;
}#pragma mark --- 添加子视图等
- (void)setViewsWithFrame:(CGRect)frame {
self.layer.borderWidth = 1.0;
self.layer.borderColor = [UIColor whiteColor].CGColor;
self.layer.cornerRadius = 3.0; // 前端图片
self.headerImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
self.headerImageView.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:self.headerImageView]; // 输入框
self.textField = [[UITextField alloc] init];
self.textField.translatesAutoresizingMaskIntoConstraints = NO; [self addSubview:self.textField]; // 尾部小按钮图标
self.iconButton = [[UIButton alloc] initWithFrame:CGRectZero];
self.iconButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.iconButton addTarget:self action:@selector(iconButtonClickAction:) forControlEvents:UIControlEventTouchUpInside]; [self addSubview:self.iconButton]; // 默认设置
self.placeholderColor = [UIColor lightGrayColor];
} #pragma mark --- 小图标点击方法
- (void)iconButtonClickAction:(UIButton *)sender {
if (self.iconButtonClickCallback) {
self.iconButtonClickCallback(self, sender, self.secureTextEntry);
}
} - (void)setImage:(UIImage *)image {
_image = image;
_headerImageView.image = _image;
} - (NSString *)text {
return _textField.text;
} - (void)setText:(NSString *)text {
_textField.text = text;
} - (void)setTextColor:(UIColor *)textColor {
_textColor = textColor;
_textField.textColor = _textColor;
} - (void)setPlaceholder:(NSString *)placeholder {
_placeholder = placeholder;
_textField.placeholder = _placeholder;
} - (void)setPlaceholderColor:(UIColor *)placeholderColor {
if (_placeholder == nil || _placeholder.length == ) {
return;
}
_placeholderColor = placeholderColor;
NSMutableAttributedString *placeholderAttributedString = [[NSMutableAttributedString alloc] initWithString:_placeholder]; [placeholderAttributedString addAttribute:NSForegroundColorAttributeName value:_placeholderColor range:NSMakeRange(, _placeholder.length)]; _textField.attributedPlaceholder = placeholderAttributedString;
} - (void)setSecureTextEntry:(BOOL)secureTextEntry {
_secureTextEntry = secureTextEntry;
_textField.secureTextEntry = _secureTextEntry;
} - (void)setIconImage:(UIImage *)iconImage {
_iconImage = iconImage;
if (_iconImage) {
[_iconButton setImage:_iconImage forState:UIControlStateNormal];
}
} - (void)setIsIconVisible:(BOOL)isIconVisible {
_isIconVisible = isIconVisible;
_iconButton.hidden = !_isIconVisible;
} #pragma mark --- 实时更新到 InterfaceBuilder
- (void)prepareForInterfaceBuilder {
[self setConstraints];
} #pragma mark --- 设置约束
- (void)setConstraints {
// 图片约束
NSLayoutConstraint *headerImageLeading = [NSLayoutConstraint constraintWithItem:self.headerImageView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeLeading multiplier:1.0 constant:8.0];
NSLayoutConstraint *headerImageTop = [NSLayoutConstraint constraintWithItem:self.headerImageView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:8.0];
NSLayoutConstraint *headerImageBottom = [NSLayoutConstraint constraintWithItem:self.headerImageView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-8.0];
NSLayoutConstraint *headerImageWidth = [NSLayoutConstraint constraintWithItem:self.headerImageView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:-8.0]; [self addConstraints:@[headerImageLeading, headerImageTop, headerImageBottom, headerImageWidth]]; // 小图标约束
NSLayoutConstraint *iconButtonTop = [NSLayoutConstraint constraintWithItem:self.iconButton attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:8.0];
NSLayoutConstraint *iconButtonBottom = [NSLayoutConstraint constraintWithItem:self.iconButton attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:-8.0];
NSLayoutConstraint *iconButtonTrailing = [NSLayoutConstraint constraintWithItem:self.iconButton attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:-8.0];
NSLayoutConstraint *iconButtonWidth = [NSLayoutConstraint constraintWithItem:self.iconButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeHeight multiplier:1.0 constant:-16.0]; if (self.isIconVisible == NO) {
// 如果图标不可见,设置宽度为0的约束
iconButtonWidth = [NSLayoutConstraint constraintWithItem:self.iconButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:0.0];
} [self addConstraints:@[iconButtonWidth, iconButtonTop, iconButtonBottom, iconButtonTrailing]]; // 输入框约束
NSLayoutConstraint *textFieldImageLeading = [NSLayoutConstraint constraintWithItem:self.textField attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.headerImageView attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:8.0];
NSLayoutConstraint *textFieldImageTop = [NSLayoutConstraint constraintWithItem:self.textField attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeTop multiplier:1.0 constant:0.0];
NSLayoutConstraint *textFieldImageBottom = [NSLayoutConstraint constraintWithItem:self.textField attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0.0];
NSLayoutConstraint *textFieldTrailing = [NSLayoutConstraint constraintWithItem:self.textField attribute:NSLayoutAttributeTrailing relatedBy:NSLayoutRelationEqual toItem:self.iconButton attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0.0]; [self addConstraints:@[textFieldImageLeading, textFieldImageTop, textFieldImageBottom, textFieldTrailing]];
} - (void)layoutSubviews {
[super layoutSubviews]; // 约束在这里添加,才能保证使用使用代码创建时正常显示
[self setConstraints];
} /*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/ @end
iOS - 创建可以在 InterfaceBuilder 中实时预览的自定义控件的更多相关文章
- Android中实时预览UI和编写UI的各种技巧
一.啰嗦 之前有读者反馈说,你搞这个所谓的最佳实践,每篇文章最后就给了一个库,感觉不是很高大上.其实,我在写这个系列之初就有想过这个问题.我的目的是:给出最实用的库来帮助我们开发,并且尽可能地说明这个 ...
- MWeb 1.4 新功能介绍一:引入文件夹到 MWeb 中管理,支持 Octpress、Jekyll 等静态博客拖拽插入图片和实时预览
之前在 MWeb 中打开非文档库中的 Markdown 文档,如果文档中有引用到本机图片,是没办法在 MWeb 中显示出来和预览的.这是因为 Apple 规定在 Mac App Store(MAS) ...
- UI实时预览最佳实践(转)
UI实时预览最佳实践 概要:Android中实时预览UI和编写UI的各种技巧.本文的例子都可以在结尾处的示例代码中看到并下载.如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以 ...
- Notepad++中实现Markdown语法高亮与实时预览
Notepad ++是一个十分强大的编辑器,除了可以用来制作一般的纯文字说明文件,也十分适合编写计算机程序代码.Notepad ++不仅有语法高亮度显示,也有语法折叠功能,并且支持宏以及扩充基本功能的 ...
- 海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码
在上一篇方案<EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)>我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一个 ...
- Sublime、Webstorm等在APICloud平台上全面支持WiFi真机同步和实时预览功能
APICloud工具插件包括APICloud Studio.Sublime Text和Webstorm全面为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同 ...
- TypeWonder – 在任何网站上实时预览字体效果
TypeWonder 让网页字体的选择过程变得轻松愉快.它可以帮助您在任何网站上快速测试 Web 字体效果!输入网站网址,就能够即时预览的字体的实际效果,还可以从数百种字体中进行挑选,您还可以得到所需 ...
- APICloud全面支持WiFi真机同步和实时预览功能
APICloud工具插件包括APICloud Studio.Sublime Text和Webstorm全面为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同 ...
- 新增WiFi真机同步与实时预览功能 简化真机调试步骤
APICloud工具插件为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同步功能,更新增WiFi真机同步和WiFi真机实时预览两大功能,方便开发者在开发过程 ...
随机推荐
- C#的选择语句练习(一)
1.请输入一个数x,若x<1,则y=x:若1<=x<10,则y=2x-1:若x>=10,则y=3x-11,要求随意输入一个x值,求出y值. 2.输入问题[你有房子吗?],若回答 ...
- mysql-python安装
操作系统:ubuntu16.04-gnome 首先要安装mysql数据 sudo apt install mysql-server 我们使用pip进行安装第三方模块 系统python版本为2.7.12 ...
- 漏洞: RHSA-2017:3075: wget security update
该网址有解决方案 http://www.stumblingblock.cn/3102.html
- Python--day46--分组(看了别人博客掌握的)
原文链接:https://www.cnblogs.com/snsdzjlz320/p/5738226.html group by group by + group_concat() group by ...
- Python--day41--threading中的定时器Timer
定时器Timer:定时开启线程 代码示例: #定时开启线程 import time from threading import Timer def func(): print('时间同步') #1-3 ...
- tomcat最大线程数的设置
Tomcat的server.xml中连接器设置如下 <Connector port="8080" maxThreads="150" minSpareThr ...
- SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用spring data redis存储数据时乱码 redis key/value 出现\xAC\xED\x00\x05t\x00\x05 问题分析: 查看RedisT ...
- idea启用列模式的方式小结
(1)alt+鼠标左键----实现的是几个连续列要向上或者向下拉,能够同时操作多行数据. (2)Shift+alt+鼠标左键----可以实现点选跨行的列模式同时操作,而且不通行可以点选不通列,进行跨行 ...
- Kobjects, Ksets 和 Subsystems
Kobject 是基础的结构, 它保持设备模型在一起. 初始地它被作为一个简单的引用计数, 但是它的责任已随时间增长, 并且因此有了它自己的战场. struct kobject 所处理的任 务和它的支 ...
- 【sublime】Pretty Json插件的安装与配置使用
一.安装 Package Control 代码安装 从菜单 View - Show Console 或者 ctrl + ~ 快捷键,调出 console.将以下 Python 代码粘贴进去并 ent ...