一、需求实现一个前后带图标的输入框

这是一个简单的自定义控件,很容易想到自定义一个视图(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 中实时预览的自定义控件的更多相关文章

  1. Android中实时预览UI和编写UI的各种技巧

    一.啰嗦 之前有读者反馈说,你搞这个所谓的最佳实践,每篇文章最后就给了一个库,感觉不是很高大上.其实,我在写这个系列之初就有想过这个问题.我的目的是:给出最实用的库来帮助我们开发,并且尽可能地说明这个 ...

  2. MWeb 1.4 新功能介绍一:引入文件夹到 MWeb 中管理,支持 Octpress、Jekyll 等静态博客拖拽插入图片和实时预览

    之前在 MWeb 中打开非文档库中的 Markdown 文档,如果文档中有引用到本机图片,是没办法在 MWeb 中显示出来和预览的.这是因为 Apple 规定在 Mac App Store(MAS) ...

  3. UI实时预览最佳实践(转)

    UI实时预览最佳实践 概要:Android中实时预览UI和编写UI的各种技巧.本文的例子都可以在结尾处的示例代码中看到并下载.如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以 ...

  4. Notepad++中实现Markdown语法高亮与实时预览

    Notepad ++是一个十分强大的编辑器,除了可以用来制作一般的纯文字说明文件,也十分适合编写计算机程序代码.Notepad ++不仅有语法高亮度显示,也有语法折叠功能,并且支持宏以及扩充基本功能的 ...

  5. 海康威视实时预览回调PS流用EasyRTMP向RTMP服务器推流中视频数据处理的代码

    在上一篇方案<EasyRTMP结合海康HCNetSDK获取海康摄像机H.264实时流并转化成为RTMP直播推流(附源码)>我们介绍了将海康安防摄像机进行互联网直播的整体方案流程,其中有一个 ...

  6. Sublime、Webstorm等在APICloud平台上全面支持WiFi真机同步和实时预览功能

    APICloud工具插件包括APICloud Studio.Sublime Text和Webstorm全面为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同 ...

  7. TypeWonder – 在任何网站上实时预览字体效果

    TypeWonder 让网页字体的选择过程变得轻松愉快.它可以帮助您在任何网站上快速测试 Web 字体效果!输入网站网址,就能够即时预览的字体的实际效果,还可以从数百种字体中进行挑选,您还可以得到所需 ...

  8. APICloud全面支持WiFi真机同步和实时预览功能

    APICloud工具插件包括APICloud Studio.Sublime Text和Webstorm全面为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同 ...

  9. 新增WiFi真机同步与实时预览功能 简化真机调试步骤

    APICloud工具插件为开发者提供iOS和Android平台真机同步调试功能,不仅可以通过USB方式进行APP真机同步功能,更新增WiFi真机同步和WiFi真机实时预览两大功能,方便开发者在开发过程 ...

随机推荐

  1. lrj word

    www.tup.tsinghua.edu.cn/upload/books/yz/055687-01.doc bing搜索UVa437 搜到这个word版本的电子书第9章

  2. pytorch lstm crf 代码理解 重点

    好久没有写博客了,这一次就将最近看的pytorch 教程中的lstm+crf的一些心得与困惑记录下来. 原文 PyTorch Tutorials 参考了很多其他大神的博客,https://blog.c ...

  3. 2004年NOIP普及组复赛题解

    题目涉及算法: 不高兴的津津:入门题: 花生采摘:贪心: FBI树:递归.DP求区间和: 火星人:模拟. 不高兴的津津 题目链接: 简单枚举. 遍历一遍,找到 \(a[i] + b[i]\) 最大的那 ...

  4. -Bash: Unzip: Command Not Found解决方法 安装unzip

    利用unzip命令解压缩的时候,出现-bash: unzip: command not found的错误. unzip——命令没有找到,其原因肯定是没有安装unzip.利用一句命令就可以解决了. 命令 ...

  5. Python图表绘制Matplotlib

    引入 import numpy as npimport pandas as pdimport matplotlib.pyplot as plt# 导入相关模块 使用 # 图表窗口1 → plt.sho ...

  6. P1065 汪老师的烟

    题目描述 汪老师有n根烟,他每吸完一根烟就把烟蒂保存起来,\(k(k>1)\) 个烟蒂可以换一个新的烟,那么 汪老师 最终能吸到多少根烟呢? 输入格式 每组测试数据一行包括两个整数 \(n,k( ...

  7. asp dotnet core 通过图片统计 csdn 用户访问

    在 csdn 的访问统计里面,只能用 csdn 提供的访问统计,因为在 csdn 中不支持在博客加上 js 代码,也就是无法使用友盟等工具统计. 通过在 asp dotnet core 创建一个图片链 ...

  8. H3C STP基本配置

  9. linux Tasklets 机制

    tasklet 类似内核定时器在某些方面. 它们一直在中断时间运行, 它们一直运行在调度它 们的同一个 CPU 上, 并且它们接收一个 unsigned long 参数. 不象内核定时器, 但是, 你 ...

  10. linux下mysql5.7忘记root密码修改

    朋友最近开始学服务器,mysql密码忘了又不会弄,让我帮忙解决一下.重置或修改mysql的root密码这种事平时很少做,还是得google辅助一下,于是弄完了写篇博客记录一下,方便若干月后又有人遇到这 ...