Skip to content

Commit 739a4f5

Browse files
authored
Fix skeletons interfering with attributed strings in text-based views (#520)
When enabling skeleton mode in a text-based view (`UILabel`, `UITextView`, `UITextField`), it sets the `textColor` to `.clear`, which is fine when `text` is used, but causes problems when `attributedText` is used, as it effectively "resets" the string to have a single color. Additionally, when a `UILabel` is nested inside a `UIStackView` a dummy string `" "` was set on the label's `text` so that it didn't have a 0 height content size. However, this workaround didn't consider the case where the label already had a non-empty text, meaning that this (intrusive) `text = " "` broke existing code by clearing the label's contents. By improving the corresponding `RecoverableXState` structs, we are able to preserve each element's contents and state as skeleton is disabled. Fixes #518. ## Changes - Create new `RecoverableLabelState` containing a `attributedText` and `text`, and use it on `UILabel`. - Update `RecoverableTextViewState` and `RecoverableTextFieldState` to have a `attributedText`. - Check if `UILabel`'s `text` is empty before setting dummy value when enabling skeleton mode in a label nested inside a `UIStackView`.
1 parent 0058d51 commit 739a4f5

3 files changed

Lines changed: 32 additions & 6 deletions

File tree

SkeletonViewCore/Sources/Internal/Models/RecoverableViewState.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,38 @@ struct RecoverableViewState {
2424

2525
}
2626

27-
struct RecoverableTextViewState {
27+
struct RecoverableLabelState {
28+
var attributedText: NSAttributedString? // we mess with `textColor`, which impacts attributed string if defined
29+
var text: String? // we mess with `text` if the label is within a `UIStackView`
2830
var textColor: UIColor?
29-
31+
3032
init(view: UILabel) {
33+
if let attributedText = view.attributedText {
34+
self.attributedText = attributedText
35+
} else {
36+
self.text = view.text
37+
}
3138
self.textColor = view.textColor
3239
}
40+
}
41+
42+
struct RecoverableTextViewState {
43+
var attributedText: NSAttributedString? // we mess with `textColor`, which impacts attributed string if defined
44+
var textColor: UIColor?
3345

3446
init(view: UITextView) {
47+
self.attributedText = view.attributedText
3548
self.textColor = view.textColor
3649
}
3750
}
3851

3952
struct RecoverableTextFieldState {
53+
var attributedText: NSAttributedString? // we mess with `textColor`, which impacts attributed string if defined
4054
var textColor: UIColor?
4155
var placeholder: String?
4256

4357
init(view: UITextField) {
58+
self.attributedText = view.attributedText
4459
self.textColor = view.textColor
4560
self.placeholder = view.placeholder
4661
}

SkeletonViewCore/Sources/Internal/SkeletonExtensions/Recoverable.swift

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ extension UIView: Recoverable {
4747

4848
extension UILabel {
4949

50-
var labelState: RecoverableTextViewState? {
51-
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableTextViewState }
50+
var labelState: RecoverableLabelState? {
51+
get { return ao_get(pkey: &ViewAssociatedKeys.labelViewState) as? RecoverableLabelState }
5252
set { ao_setOptional(newValue, pkey: &ViewAssociatedKeys.labelViewState) }
5353
}
5454

5555
override func saveViewState() {
5656
super.saveViewState()
57-
labelState = RecoverableTextViewState(view: self)
57+
labelState = RecoverableLabelState(view: self)
5858
}
5959

6060
override func recoverViewState(forced: Bool) {
@@ -70,6 +70,11 @@ extension UILabel {
7070

7171
if self.textColor == .clear || forced {
7272
self.textColor = storedLabelState.textColor
73+
if let attributedText = storedLabelState.attributedText {
74+
self.attributedText = attributedText
75+
} else {
76+
self.text = storedLabelState.text
77+
}
7378
}
7479
}
7580
}
@@ -95,6 +100,9 @@ extension UITextView {
95100

96101
if self?.textColor == .clear || forced {
97102
self?.textColor = storedLabelState.textColor
103+
if let attributedText = storedLabelState.attributedText {
104+
self?.attributedText = attributedText
105+
}
98106
}
99107
}
100108
}
@@ -120,6 +128,9 @@ extension UITextField {
120128

121129
if self?.textColor == .clear || forced {
122130
self?.textColor = storedLabelState.textColor
131+
if let attributedText = storedLabelState.attributedText {
132+
self?.attributedText = attributedText
133+
}
123134
}
124135

125136
if self?.placeholder == nil || forced {

SkeletonViewCore/Sources/Internal/UIKitExtensions/UILabel+Extensions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ extension UILabel {
2727
guard estimatedNumberOfLines > 1 || estimatedNumberOfLines == 0 else { return }
2828

2929
// Workaround to simulate content when the label is contained in a `UIStackView`.
30-
if isSuperviewAStackView, bounds.height == 0 {
30+
if isSuperviewAStackView, bounds.height == 0, (text?.isEmpty ?? true) {
3131
// This is a placeholder text to simulate content because it's contained in a stack view in order to prevent that the content size will be zero.
3232
text = " "
3333
}

0 commit comments

Comments
 (0)