需求背景:
- 在textfield的输入内容里加前后加上固定的字符
"符号A+ 输入内容+ 符号B"
最初的想法是直接在相应的回调中进行处理。
但这样会涉及取值,以及一些复杂情况的处理,比如粘贴,插入等情况的处理。放弃。
然后想着弄三个元素 label+textfield+label的形式。既能方便取值,各种情况也不需要处理,不用关心符号。
接着快速写了测试项目,masonry加了约束。xcode9跑iOS11模拟器,无错。
直接放进项目,继续模拟器运行,完美。然后在我提交代码后的第二天,收到了bug反馈。
说输入的文字宽度没变化。产生挤压,在textfield失去焦点的时候才会正常显示。
猜想:
- 是不是约束做的有问题,hugging优先级没弄好?
- 是不是需要在输入的时候重新布局或渲染视图?
经过反复调试优先级和验证,约束无错。。。
尝试调用setNeedsLayout / LayoutIfNeeded等方法,无效。
- 是不是uitextfield相关的属性有哪些地方没有考虑到?
把textfield相关的官方文档看一遍。
因为要解决这个问题,其实也就是实现另外一个效果:在输入的时候把textfield的宽度变成输入内容等宽。也就涉及到size相关的处理。
第一反应:那就在相应的回调里面,通过计算文字的宽度,改变约束去强制改变textfield的宽度。但强迫症觉得这个方法太恶心,肯定有别的好方法。
面向stackoverflow:
google关键字:textfield changing width when input ios
https://stackoverflow.com/questions/18236661/resize-a-uitextfield-while-typing-by-using-autolayout
https://stackoverflow.com/questions/41436716/how-to-increase-width-of-textfield-according-to-typed-text
这两个提问和我的需求几乎一致,楼下的解决办法都有提到同一个东东:intrinsicContentSize
跟着方法撸一次
- subclass of UITextField
- override intrinsicContentSize
- (CGSize)intrinsicContentSize {
return self.isEditing ? [super.text sizeWithAttributes:self.typingAttributes] : [super intrinsicContentSize];
}
- handle event for 'UIControlEventEditingChanged'
//UIControlEventEditingChanged
- (IBAction)changedEvent:(UITextField *)sender {
[sender invalidateIntrinsicContentSize];
}
跑跑,真的可以。but, why?
在invalidateIntrinsicContentSize方法看到相关解释。
call this when something changes that affects the intrinsicContentSize. Otherwise UIKit won't notice that it changed.
如果有什么鬼东东影响了它的固有尺寸的时候,就调用这个方法,不然 uikit 根本不知道他产生了变化。
在intrinsicContentSize方法看到相关解释
/* Override this method to tell the layout system that there is something it doesn't natively understand in this view, and this is how large it intrinsically is. A typical example would be a single line text field. The layout system does not understand text - it must just be told that there's something in the view, and that that something will take a certain amount of space if not clipped.
In response, UIKit will set up constraints that specify (1) that the opaque content should not be compressed or clipped, (2) that the view prefers to hug tightly to its content.
A user of a view may need to specify the priority of these constraints. For example, by default, a push button
-strongly wants to hug its content in the vertical direction (buttons really ought to be their natural height)
-weakly hugs its content horizontally (extra side padding between the title and the edge of the bezel is acceptable)
-strongly resists compressing or clipping content in both directions.
However, you might have a case where you'd prefer to show all the available buttons with truncated text rather than losing some of the buttons. The truncation might only happen in portrait orientation but not in landscape, for example. In that case you'd want to setContentCompressionResistancePriority:forAxis: to (say) UILayoutPriorityDefaultLow for the horizontal axis.
The default 'strong' and 'weak' priorities referred to above are UILayoutPriorityDefaultHigh and UILayoutPriorityDefaultLow.
Note that not all views have an intrinsicContentSize. UIView's default implementation is to return (UIViewNoIntrinsicMetric, UIViewNoIntrinsicMetric). The _intrinsic_ content size is concerned only with data that is in the view itself, not in other views. Remember that you can also set constant width or height constraints on any view, and you don't need to override instrinsicContentSize if these dimensions won't be changing with changing view content.
这么多就不翻译了。。自己看吧。不难。大致意思就是重写这个intrincsize方法告诉系统这个视图有多大,也就是你自己定义大小,还有一些约束什么的。
*/
结
也就是说,重写了之后,都会根据它的typingAttributes计算相应的尺寸,然后在回调里面手动触发这个计算,每输入一次就计算一次尺寸。这样textfield的宽度就变成了文字的宽度了。
网友评论