需求背景:
- 在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的寬度就變成了文字的寬度了。