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`.
50 lines
1.8 KiB
Swift
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()
|
|
}
|
|
|
|
}
|