Files
André Pacheco Neves 739a4f5d78 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`.
2022-10-20 12:59:00 +02:00

50 lines
1.8 KiB
Swift

//
// Copyright SkeletonView. All Rights Reserved.
//
// Licensed under the MIT License (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://opensource.org/licenses/MIT
//
// UILabel+Extensions.swift
//
// Created by Juanpe Catalán on 19/8/21.
import UIKit
extension UILabel {
var desiredHeightBasedOnNumberOfLines: CGFloat {
let spaceNeededForEachLine = estimatedLineHeight * CGFloat(estimatedNumberOfLines)
let spaceNeededForSpaces = skeletonLineSpacing * CGFloat(estimatedNumberOfLines - 1)
let padding = paddingInsets.top + paddingInsets.bottom
return spaceNeededForEachLine + spaceNeededForSpaces + padding
}
func updateHeightConstraintsIfNeeded() {
guard estimatedNumberOfLines > 1 || estimatedNumberOfLines == 0 else { return }
// Workaround to simulate content when the label is contained in a `UIStackView`.
if isSuperviewAStackView, bounds.height == 0, (text?.isEmpty ?? true) {
// 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.
text = " "
}
let desiredHeight = desiredHeightBasedOnNumberOfLines
if desiredHeight > definedMaxHeight {
backupHeightConstraints = heightConstraints
NSLayoutConstraint.deactivate(heightConstraints)
setHeight(equalToConstant: desiredHeight)
}
}
func restoreBackupHeightConstraintsIfNeeded() {
guard !backupHeightConstraints.isEmpty else { return }
NSLayoutConstraint.activate(backupHeightConstraints)
backupHeightConstraints.removeAll()
}
}