整理了一份Demo,因为每个项目具体的需求不一样,我只把基本的功能整理出来了

Demo放在GitHub上

项目中要实现填空题的作答功能,比如诗词填空:床前明月光,___________。举头望明月,________。要求只能编辑横线部分。

首先想到的是强大的YYKit,先在网上找了找,发现有一种方案是用label 加textfield的这种富文本编辑的方式实现的,虽然大体符合需求,但是排版会比较难看。

最后决定用YYTextView去实现,原理就是根据正则匹配题干和下划线,整个题目会被填空部分分割成几块,把各个分块binding,然后控制光标位置,只让光标落在下划线上。

创建题目:

NSMutableAttributedString *text = [[NSMutableAttributedString alloc] initWithString:@"#(填空题)枯藤老树昏鸦,# #,古道西风瘦马。# #,断肠人在天涯。# "];

text.yy_font = [UIFont systemFontOfSize:17];

text.yy_lineSpacing = 5;

text.yy_color = [UIColor blackColor];

YYTextView *textView = [YYTextView new];

textView.textParser = [YYTextEditBindingParser new];

textView.attributedText = text;

textView.frame = CGRectMake(5, 100, CGRectGetWidth(self.view.frame)-10, 200);

textView.textContainerInset = UIEdgeInsetsMake(10, 10, 10, 10);

textView.delegate = self;

if (kiOS7Later) {

textView.keyboardDismissMode = UIScrollViewKeyboardDismissModeInteractive;

}

textView.scrollIndicatorInsets = textView.contentInset;

[self.view addSubview:textView];

self.textView = textView;

在代理方法里面控制光标位置

#pragma mark YYTextViewDelegate

- (BOOL)textViewShouldBeginEditing:(YYTextView *)textView{

if (textView.selectedRange.location==0 || textView.selectedRange.location >= textView.text.length) {

return NO;

}else{

return [self controllCursorRangeForTextView:textView];

}

}

- (void)textViewDidChangeSelection:(YYTextView *)textView{

if (textView.selectedRange.location==0 || textView.selectedRange.location >= textView.text.length) {

[textView endEditing:YES];

}else {

[self controllCursorRangeForTextView:textView];

}

}

控制光标

- (BOOL)controllCursorRangeForTextView:(YYTextView *)textView{

YYTextEditBindingParser *textParser = textView.textParser;

for (NSString *rangeStr in textParser.gapRangeArr) {

NSRange range = NSRangeFromString(rangeStr);

if (textView.selectedRange.location >= range.location && textView.selectedRange.location < range.location + 3) {

textView.selectedRange = NSMakeRange(range.location + 3,0);

return YES;

}else if(textView.selectedRange.location > (range.location + range.length -3)&&textView.selectedRange.location <= (range.location + range.length)){

textView.selectedRange = NSMakeRange(range.location + range.length - 3,0);

return YES;

}

}

return YES;

}

为了不让删除binding的字符串我在YYtextView里面加了个代理方法,在我这里实现一下:

- (BOOL)textViewShouldDeleteBinding:(YYTextView *)textView{

return NO;

}

YYTextEditBindingParser这个类用于binding和对输入内容加下划线,有个属性gapRangeArr用来保存填空部分的range

@interface YYTextEditBindingParser :NSObject

@property (nonatomic, strong) NSRegularExpression *regex;

@property (nonatomic, strong) NSRegularExpression *gapRegex;

@property(nonatomic, strong)NSArray *gapRangeArr;

@end

@implementation YYTextEditBindingParser

- (instancetype)init {

self = [super init];

NSString *pattern1 = @"#([^#]*)#";

self.regex = [[NSRegularExpression alloc] initWithPattern:pattern1 options:kNilOptions error:nil];

NSString *pattern2 = @"\\s{3}([^\\s{3}]*)\\s{3}";

self.gapRegex = [[NSRegularExpression alloc] initWithPattern:pattern2 options:kNilOptions error:nil];

return self;

}

- (NSRange)_replaceTextInRange:(NSRange)range withLength:(NSUInteger)length selectedRange:(NSRange)selectedRange {

// no change

if (range.length == length) return selectedRange;

// right

if (range.location >= selectedRange.location + selectedRange.length) return selectedRange;

// left

if (selectedRange.location >= range.location + range.length) {

selectedRange.location = selectedRange.location + length - range.length;

return selectedRange;

}

// same

if (NSEqualRanges(range, selectedRange)) {

selectedRange.length = length;

return selectedRange;

}

// one edge same

if ((range.location == selectedRange.location && range.length < selectedRange.length) ||

(range.location + range.length == selectedRange.location + selectedRange.length && range.length < selectedRange.length)) {

selectedRange.length = selectedRange.length + length - range.length;

return selectedRange;

}

selectedRange.location = range.location + length;

selectedRange.length = 0;

return selectedRange;

}

- (BOOL)parseText:(NSMutableAttributedString *)text selectedRange:(NSRangePointer)range {

__block BOOL changed = NO;

NSArray *matches = [_regex matchesInString:text.string options:kNilOptions range:NSMakeRange(0, text.length)];

NSRange selectedRange = range ? *range : NSMakeRange(0, 0);

NSUInteger cutLength = 0;

for (NSUInteger i = 0, max = matches.count; i < max; i++) {

NSTextCheckingResult *one = matches[i];

NSRange oneRange = one.range;

if (oneRange.length == 0) continue;

oneRange.location -= cutLength;

NSString *subStr = [text.string substringWithRange:NSMakeRange(oneRange.location+1, oneRange.length-2)];

CGFloat fontSize = 12; // CoreText default value

CTFontRef font = (__bridge CTFontRef)([text yy_attribute:NSFontAttributeName atIndex:oneRange.location]);

if (font) fontSize = CTFontGetSize(font);

NSMutableAttributedString *atr = [[NSMutableAttributedString alloc] initWithString:subStr];

[text replaceCharactersInRange:oneRange withString:atr.string];

[text yy_removeDiscontinuousAttributesInRange:NSMakeRange(oneRange.location, atr.length)];

[text addAttributes:atr.yy_attributes range:NSMakeRange(oneRange.location, atr.length)];

selectedRange = [self _replaceTextInRange:oneRange withLength:atr.length selectedRange:selectedRange];

NSRange bindlingRange = NSMakeRange(oneRange.location, oneRange.length-2);

YYTextBinding *binding = [YYTextBinding bindingWithDeleteConfirm:YES];

[text yy_setTextBinding:binding range:bindlingRange]; /// Text binding

[text yy_setColor:[UIColor colorWithRed:0.000 green:0.519 blue:1.000 alpha:1.000] range:bindlingRange];

cutLength += 2;

}

NSArray *gapMatches = [_gapRegex matchesInString:text.string options:kNilOptions range:NSMakeRange(0, text.length)];

if (gapMatches.count == 0) return NO;

// NSRange lastOneRange = NSMakeRange(0, 0);

NSMutableArray *gapRangeTempArr = [NSMutableArray array];

for (NSUInteger i = 0, max = gapMatches.count; i < max; i++) {

NSTextCheckingResult *one = gapMatches[i];

NSRange oneRange = one.range;

YYTextDecoration *decoration = [YYTextDecoration new];

[text yy_setTextUnderline:decoration range:oneRange];

[gapRangeTempArr addObject:NSStringFromRange(oneRange)];

}

self.gapRangeArr = gapRangeTempArr;

if (range) *range = selectedRange;

return changed;

}

@end

在YYTextView.m的deleteBackward方法里加了一段代码:

if (binding && binding.deleteConfirm) {

if ([self.delegate respondsToSelector:@selector(textViewShouldDeleteBinding:)]) {

if (![self.delegate textViewShouldDeleteBinding:self]) {

return;

}

}

_state.deleteConfirm = YES;

[_inputDelegate selectionWillChange:self];

_selectedTextRange = [YYTextRange rangeWithRange:effectiveRange];

_selectedTextRange = [self _correctedTextRange:_selectedTextRange];

[_inputDelegate selectionDidChange:self];

[self _updateOuterProperties];

[self _updateSelectionView];

return;

}

就是在删除的时候,如果是binding的字符串,就return;

好啦,就这样吧!第一次发文章

yytextview 复制_用YYTextView 实现填空题作答功能相关推荐

  1. 2020年上海市高等学校信息技术水平考试试卷_三级_数据科学技术及应用_模拟卷_三、程序填空题_答案

    2020年上海市高等学校信息技术水平考试试卷_三级_数据科学技术及应用_模拟卷_三.程序填空题_答案 (本试卷考试时间 150 分钟) 答案是自己做的,经验证,可成功运行. 内容仅供学习交流,不可转载 ...

  2. 2009年9月三级网络技术考前预测_填空题部分

    <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 2009年9月 ...

  3. 前端开发下划线怎么设置_怎么使用Word快速制作填空题下划线? 只须一个快捷键, 教师必备...

    Microsoft Word是最常使用的文字处理软件之一.在使用Microsoft Word制作试卷和封面时,怎么制作填空题的下划线呢?这里介绍三种方法,操作很简单,方法很实用. 方法一:使用Unic ...

  4. 考研数据结构填空题整合_做题版

    考研数据结构填空题整合 目录 考研数据结构填空题整合 一.ZYL组 ZYL组一 ZYL组二 ZYL组三 ZYL组四 ZYL组五 ZYL组六 ZYL组七 ZYL组八 二.TJP组 TJP组一 TJP组二 ...

  5. java语言与www技术形成性考核册_电大Java语言与WWW技术形成性考核填空题

    2017电大最新题库 电大Java语言与WWW技术形成性考核填空题 1.Java具有的特点简单.面向对象.与平台无关.解释型.多线程.安全.动态的语言. 2.开发与运行Java程序需要经过的三个主要步 ...

  6. java程序设计试题_《Java语言程序设计》期末考试模拟试题——填空题和编程题...

    一.根据题意,填写出空格中的内容 Java平台包括三个技术方向,其中J2ME代表____________.J2SE代表___________.J2EE代表____________.2.面向对象的四大概 ...

  7. c语言共有几种运算符_【填空题】C语言一共有 ()个关键字,()中控制语句,()种运算符...

    [填空题]C语言一共有 ()个关键字,()中控制语句,()种运算符 更多相关问题 [填空题] 对煤进行工业分析的目的,是为了判断煤的(). [填空题] 钛的比重是不锈钢的一半,抗腐蚀性是不锈钢的(). ...

  8. java中的复合数据类型是什么_【填空题】类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素。一个类的实现包括两部分:____和_____....

    [填空题]类是Java中的一种重要的复合数据类型,是组成Java程序的基本要素.一个类的实现包括两部分:____和_____. 更多相关问题 [名词解释] 观叶树木 [单选] 开花时有浓郁香气的树种是 ...

  9. java填空题 在非静态成员方法中_成本加成定价法的优点有

    [单选题]以下Math类的方法中,-4.4通过哪个方法运算后,结果为-5.0? [填空题]以下程序的输出结果为? [单选题]下列方法定义中,正确的是() [判断题]Java 中被 final 关键字修 ...

最新文章

  1. Go 分布式学习利器(13)-- Go语言的多态
  2. 分布式存储 Ceph 介绍及原理架构分享--云平台技术栈系列01
  3. 网络数据修改工具netsed
  4. [基础常识]申请免费SSL证书 - 阿里云云盾证书 - Digicert+Symantec 免费型DV SSL
  5. 前端学习(2836):view和text标签
  6. Linux下编译FFMpeg
  7. 蓝桥杯第七届决赛JAVA真题----广场舞
  8. 百分点零售行业大数据解决方案
  9. javascript模块化编程思想(转载网上专家)Javascript模块化编程(一)
  10. Win11怎么设置鼠标箭头图案?Win11更换鼠标图案的方法
  11. 语音信号处理——线性预测编码LPC
  12. Android可收缩伸展的Expandable分组RecyclerView
  13. 个人公众号注销方法_微信公众号注销后可以再申请吗 公众号注销帐号方法介绍...
  14. 【聚水潭】胜算操作手册
  15. python简单的人脸识别系统(PCA+逻辑回归)
  16. 基础选择器之id选择器
  17. 【WCN685X】WCN6856 信道和20M/40M/80M/160M频宽对应参数hostapd的配置
  18. bind9 dlz mysql_源码安装Bind 9.10 正式版 开启DLZ数据库支持 和 数据库view查询
  19. 基于Graphhopper的路线导航方案
  20. 1. Unity的下载与安装

热门文章

  1. 学习Linux第一天
  2. Android测试之Monkey
  3. GO语言的进阶之路-Golang高级数据结构定义
  4. Linux开机启动分析与系统配置
  5. div中嵌套div水平垂直居中
  6. 前端开发不容错过的jQuery图片滑块插件(转)
  7. 使用JAVASCRIPT进行全屏显示页面,就像触摸屏显示效果
  8. ip地址和MAC地址的捆绑
  9. Step by Step 创建一个 Web Service
  10. django Rest Framework----认证/访问权限控制/访问频率限制 执行流程 Authentication/Permissions/Throttling 源码分析...