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

这是一个简单的自定义控件,很容易想到自定义一个视图(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. Wood Processing牛客第十场 斜率优化DP

    卧槽我感觉写的是对的,但是就是样例都过不了...留坑 #include<iostream> #include<stdio.h> #include<string.h> ...

  2. css 文字超出部分隐藏

    未做隐藏处理 执行结果: 1.1行超出部分省略号 效果: 2.多行超出部分隐藏(目前只能在chrome浏览器中使用,其他浏览器不兼容) 效果: -webkit-line-clamp 属性定义显示行数可 ...

  3. 【CSS3】loading动画

    HTML: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...

  4. 用diiv实现多个方块居中嵌套--padding

    文章地址 https://www.cnblogs.com/sandraryan/ 案例:用diiv嵌套多个正方形,配合盒模型相关知识,使每个div在他的父元素上居中.(每个div中心点对齐) 涉及到m ...

  5. Native memory allocation (mmap) failed to map xxx bytes for committing reserved memory

    遇到问题 在服务器上运行 nexus 出现Native memory allocation (mmap) failed to map 838860800 bytes for committing re ...

  6. java声明异常(throws)

    在可能出现异常的方法上声明抛出可能出现异常的类型: 声明的时候尽可能声明具体的异常,方便更好的处理. 当前方法不知道如何处理这种异常,可将该异常交给上一级调用者来处理(非RuntimeExceptio ...

  7. java的四种代码块

    用{}括起来的称为代码块: 普通代码块:类中方法的方法体 构造代码块:类中{}直接括起来的语句,每次创建对象都会被调用,先于构造函数执行 静态代码块:类中static{}括起来的语句,只执行一次,先于 ...

  8. 2018.11.30 浪在ACM 集训队第七次测试赛

    https://blog.csdn.net/StilllFantasy/article/details/84670643 感谢刘凯同学 https://blog.csdn.net/UnKfrozen/ ...

  9. Linux 内核热插拔操作

    热插拔事件的实际控制是通过一套存储于 kset_hotplug_ops 结构的方法完成. struct kset_hotplug_ops { int (*filter)(struct kset *ks ...

  10. vue-learning:3-template-{{}}-and-v-html

    插值{{ }} 和 v-html 本节开始,我们按如下顺序学习vue模板API-指令.点击各部分的DEMO可在线查看代码 输出字符串文本内容的插值:{{}} 输出HMTL元素作为内容的指令:v-htm ...