feat: Added: maxWidth parameter to BaseViewSkeletonsConfiguration. #19
|
|
@ -1,5 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
### 1.54.0
|
||||
|
||||
- **Added**: `maxWidth` parameter to `BaseViewSkeletonsConfiguration`.
|
||||
- **Added**: custom `SkeletonConfigurations` for nested `SkeletonPresenters`.
|
||||
- **Update**: Many fixes and improvenments to `TextSkeletonsConfiguration`.
|
||||
|
||||
### 1.53.3
|
||||
|
||||
- **Update**: `Skeletonable` can now control custom geometry change notification.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAppleMapUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Apple MapKit.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIApplication'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Application architecture.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIAuth'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Login, registration, confirmation and other related actions'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIBottomSheet'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Base models for creating bottom sheet view controllers'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TICoreGraphicsUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'CoreGraphics drawing helpers'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIDeeplink'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Deeplink service API'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIDeveloperUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Universal web view API'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIEcommerce'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Cart, products, promocodes, bonuses and other related actions'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIFoundationUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for Foundation framework classes.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIGoogleMapUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Google Maps SDK.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIKeychainUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for Keychain classes.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TILogging'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Logging for TI libraries.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMapUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIMoyaNetworking'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Moya + Swagger network service.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworking'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Swagger-frendly networking layer helpers.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TINetworkingCache'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Caching results of EndpointRequests.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIPagination'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Generic pagination component.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUICore'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Core UI elements: protocols, views and helpers.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TISwiftUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Bunch of useful helpers for Swift development.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITableKitUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for TableKit classes.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TITextProcessing'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'A text processing service helping to get a text mask and a placeholder from incoming regex.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -32,28 +32,46 @@ open class BaseViewSkeletonsConfiguration {
|
|||
}
|
||||
|
||||
public var padding: UIEdgeInsets
|
||||
public var maxWidth: CGFloat
|
||||
public var shape: Shape
|
||||
|
||||
public init(padding: UIEdgeInsets = .edges(5), shape: Shape = .rectangle(cornerRadius: .zero)) {
|
||||
public init(padding: UIEdgeInsets = .edges(5),
|
||||
maxWidth: CGFloat = .infinity,
|
||||
shape: Shape = .rectangle(cornerRadius: .zero)) {
|
||||
|
||||
self.shape = shape
|
||||
self.maxWidth = maxWidth
|
||||
self.padding = padding
|
||||
}
|
||||
|
||||
open func drawPath(rect: CGRect) -> CGPath {
|
||||
open func createPath(for rect: CGRect) -> CGPath {
|
||||
let adjustedRect = rect.reduceSize(byPadding: padding)
|
||||
.with(maxWidth: maxWidth)
|
||||
|
||||
switch shape {
|
||||
case let .custom(path):
|
||||
return path
|
||||
|
||||
case let .rectangle(cornerRadius: cornerRadius):
|
||||
let path = UIBezierPath(roundedRect: rect.reduceSize(byPadding: padding), cornerRadius: cornerRadius)
|
||||
return path.cgPath
|
||||
return UIBezierPath(roundedRect: adjustedRect,
|
||||
cornerRadius: cornerRadius).cgPath
|
||||
|
||||
case .circle:
|
||||
return CGPath(ellipseIn: rect.reduceSize(byPadding: padding), transform: nil)
|
||||
return CGPath(ellipseIn: adjustedRect,
|
||||
transform: nil)
|
||||
}
|
||||
}
|
||||
|
||||
open func applyPadding(viewFrame: CGRect) -> CGRect {
|
||||
viewFrame.with(padding: padding)
|
||||
}
|
||||
|
||||
open func copyWith(padding: UIEdgeInsets? = nil,
|
||||
maxWidth: CGFloat? = nil,
|
||||
shape: Shape? = nil) -> BaseViewSkeletonsConfiguration {
|
||||
|
||||
BaseViewSkeletonsConfiguration(padding: padding ?? self.padding,
|
||||
maxWidth: maxWidth ?? self.maxWidth,
|
||||
shape: shape ?? self.shape)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,10 +28,22 @@ open class ContainerViewSkeletonsConfiguration: BaseViewSkeletonsConfiguration {
|
|||
|
||||
public init(borderWidth: CGFloat = .zero,
|
||||
padding: UIEdgeInsets = .zero,
|
||||
maxWidth: CGFloat = .infinity,
|
||||
shape: Shape = .rectangle(cornerRadius: .zero)) {
|
||||
|
||||
self.borderWidth = borderWidth
|
||||
|
||||
super.init(padding: padding, shape: shape)
|
||||
}
|
||||
|
||||
open func copyWith(borderWidth: CGFloat? = nil,
|
||||
padding: UIEdgeInsets? = nil,
|
||||
maxWidth: CGFloat? = nil,
|
||||
shape: Shape? = nil) -> ContainerViewSkeletonsConfiguration {
|
||||
|
||||
ContainerViewSkeletonsConfiguration(borderWidth: borderWidth ?? self.borderWidth,
|
||||
padding: padding ?? self.padding,
|
||||
maxWidth: maxWidth ?? self.maxWidth,
|
||||
shape: shape ?? self.shape)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,35 +26,68 @@ import UIKit
|
|||
|
||||
open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration {
|
||||
|
||||
private enum Constants {
|
||||
static var defaultNumberOfLines: Int {
|
||||
public enum Defaults {
|
||||
public static var numberOfLines: Int {
|
||||
1
|
||||
}
|
||||
|
||||
public static var multilineNumberOfLines: Int {
|
||||
3
|
||||
}
|
||||
|
||||
public static var linesWidthFraction: [CGFloat] {
|
||||
[1, 0.65, 0.78]
|
||||
}
|
||||
|
||||
public static var defaultFont: UIFont {
|
||||
.systemFont(ofSize: 15)
|
||||
}
|
||||
}
|
||||
|
||||
private var isMultiline = false
|
||||
private var labelNumberOfLines: Int = .zero
|
||||
private var labelHeight: CGFloat = .zero
|
||||
private var font: UIFont?
|
||||
public var numberOfLines: Int
|
||||
public var linesWidthFraction: [CGFloat]
|
||||
public var lineHeight: CGFloat?
|
||||
public var lineSpacing: CGFloat?
|
||||
|
||||
public var numberOfLines: Int?
|
||||
public var lineHeight: Closure<UIFont?, CGFloat>?
|
||||
public var lineSpacing: Closure<UIFont?, CGFloat>?
|
||||
|
||||
public init(numberOfLines: Int? = nil,
|
||||
lineHeight: Closure<UIFont?, CGFloat>? = nil,
|
||||
lineSpacing: Closure<UIFont?, CGFloat>? = nil,
|
||||
public init(numberOfLines: Int = Defaults.numberOfLines,
|
||||
linesWidthFraction: [CGFloat] = Defaults.linesWidthFraction,
|
||||
lineHeight: CGFloat? = nil,
|
||||
lineSpacing: CGFloat? = nil,
|
||||
padding: UIEdgeInsets = .edges(5),
|
||||
maxWidth: CGFloat = .infinity,
|
||||
shape: Shape = .rectangle(cornerRadius: .zero)) {
|
||||
|
||||
self.numberOfLines = numberOfLines
|
||||
self.linesWidthFraction = linesWidthFraction
|
||||
self.lineHeight = lineHeight
|
||||
self.lineSpacing = lineSpacing
|
||||
|
||||
super.init(padding: padding, shape: shape)
|
||||
}
|
||||
|
||||
open override func drawPath(rect: CGRect) -> CGPath {
|
||||
open func createConfiguration(for label: UILabel) -> TextSkeletonsConfiguration {
|
||||
let labelFont = getFont(from: label)
|
||||
|
||||
return copyWith(numberOfLines: label.numberOfLines,
|
||||
lineHeight: calculateLineHeight(for: labelFont),
|
||||
lineSpacing: calculateLineSpacing(for: labelFont),
|
||||
padding: padding,
|
||||
maxWidth: maxWidth,
|
||||
shape: shape)
|
||||
}
|
||||
|
||||
open func createConfiguration(for textView: UITextView) -> TextSkeletonsConfiguration {
|
||||
let labelFont = getFont(from: textView)
|
||||
|
||||
return copyWith(numberOfLines: textView.textContainer.maximumNumberOfLines,
|
||||
lineHeight: calculateLineHeight(for: labelFont),
|
||||
lineSpacing: calculateLineSpacing(for: labelFont),
|
||||
padding: padding,
|
||||
maxWidth: maxWidth,
|
||||
shape: shape)
|
||||
}
|
||||
|
||||
open override func createPath(for rect: CGRect) -> CGPath {
|
||||
/*
|
||||
SkeletonLayer
|
||||
|-------------------------|
|
||||
|
|
@ -65,10 +98,10 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration {
|
|||
||-----------------------|| - third line CGRect(0, (lineHeight + spacing) * 2, rect.width, lineHeight)
|
||||
|-------------------------|
|
||||
*/
|
||||
let adjustedRect = rect.reduceSize(byPadding: padding).with(maxWidth: maxWidth)
|
||||
let path = UIBezierPath()
|
||||
let numberOfLines = getNumberOfLines()
|
||||
let spacing = getLineSpacing()
|
||||
let lineHeight = getLineHeight()
|
||||
let spacing = lineSpacing ?? calculateLineSpacing(for: Defaults.defaultFont)
|
||||
let lineHeight = self.lineHeight ?? calculateLineHeight(for: Defaults.defaultFont)
|
||||
var cornerRadius = CGFloat.zero
|
||||
|
||||
if case let .rectangle(cornerRadius: radius) = shape {
|
||||
|
|
@ -76,17 +109,27 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration {
|
|||
}
|
||||
|
||||
for lineNumber in 0..<numberOfLines {
|
||||
let adjustedLineRect: CGRect
|
||||
|
||||
if linesWidthFraction.isEmpty {
|
||||
adjustedLineRect = adjustedRect
|
||||
} else {
|
||||
let currentLineWidthFracion = linesWidthFraction[lineNumber % linesWidthFraction.count]
|
||||
let currentLineWidth = adjustedRect.width * currentLineWidthFracion
|
||||
adjustedLineRect = adjustedRect.with(maxWidth: currentLineWidth)
|
||||
}
|
||||
|
||||
let y = (lineHeight + spacing) * CGFloat(lineNumber)
|
||||
|
||||
path.move(to: CGPoint(x: cornerRadius, y: y))
|
||||
|
||||
path.addLine(to: CGPoint(x: rect.width - cornerRadius, y: y))
|
||||
path.addQuadCurve(to: CGPoint(x: rect.width, y: y + cornerRadius),
|
||||
controlPoint: CGPoint(x: rect.width, y: y))
|
||||
path.addLine(to: CGPoint(x: adjustedLineRect.width - cornerRadius, y: y))
|
||||
path.addQuadCurve(to: CGPoint(x: adjustedLineRect.width, y: y + cornerRadius),
|
||||
controlPoint: CGPoint(x: adjustedLineRect.width, y: y))
|
||||
|
||||
path.addLine(to: CGPoint(x: rect.width, y: y + lineHeight - cornerRadius))
|
||||
path.addQuadCurve(to: CGPoint(x: rect.width - cornerRadius, y: y + lineHeight),
|
||||
controlPoint: CGPoint(x: rect.width, y: y + lineHeight))
|
||||
path.addLine(to: CGPoint(x: adjustedLineRect.width, y: y + lineHeight - cornerRadius))
|
||||
path.addQuadCurve(to: CGPoint(x: adjustedLineRect.width - cornerRadius, y: y + lineHeight),
|
||||
controlPoint: CGPoint(x: adjustedLineRect.width, y: y + lineHeight))
|
||||
|
||||
path.addLine(to: CGPoint(x: cornerRadius, y: y + lineHeight))
|
||||
path.addQuadCurve(to: CGPoint(x: .zero, y: y + lineHeight - cornerRadius),
|
||||
|
|
@ -100,62 +143,63 @@ open class TextSkeletonsConfiguration: BaseViewSkeletonsConfiguration {
|
|||
return path.cgPath
|
||||
}
|
||||
|
||||
open func configureLabelPath(label: UILabel) -> CGPath {
|
||||
if case let .custom(path) = shape {
|
||||
return path
|
||||
open func getFont(from textView: UITextView) -> UIFont {
|
||||
guard let attributedText = textView.attributedText else {
|
||||
return textView.font ?? Defaults.defaultFont
|
||||
}
|
||||
|
||||
isMultiline = label.isMultiline
|
||||
font = label.font
|
||||
labelNumberOfLines = label.numberOfLines
|
||||
labelHeight = label.bounds.height
|
||||
|
||||
return drawPath(rect: label.bounds.reduceSize(byPadding: padding))
|
||||
return getBiggestFont(from: attributedText)
|
||||
}
|
||||
|
||||
open func configureTextViewPath(textView: UITextView) -> CGPath {
|
||||
if case let .custom(path) = shape {
|
||||
return path
|
||||
open func getFont(from label: UILabel) -> UIFont {
|
||||
guard let attributedText = label.attributedText else {
|
||||
return label.font
|
||||
}
|
||||
|
||||
isMultiline = textView.isMultiline
|
||||
font = textView.font
|
||||
labelNumberOfLines = textView.textContainer.maximumNumberOfLines
|
||||
labelHeight = textView.bounds.height
|
||||
|
||||
return drawPath(rect: textView.bounds.reduceSize(byPadding: padding))
|
||||
return getBiggestFont(from: attributedText)
|
||||
}
|
||||
|
||||
// MARK: - Private methods
|
||||
open func getBiggestFont(from attributedString: NSAttributedString) -> UIFont {
|
||||
let text = attributedString.string
|
||||
|
||||
private func getLineHeight() -> CGFloat {
|
||||
if let lineHeight = lineHeight?(font) {
|
||||
return lineHeight
|
||||
var biggestFont: UIFont = .systemFont(ofSize: 1)
|
||||
|
||||
attributedString.enumerateAttributes(in: NSRange(text.startIndex..., in: text)) { attributes, _, _ in
|
||||
guard let fontValue = attributes[.font] as? UIFont else {
|
||||
return
|
||||
}
|
||||
|
||||
if fontValue.lineHeight > biggestFont.lineHeight {
|
||||
biggestFont = fontValue
|
||||
}
|
||||
}
|
||||
|
||||
return biggestFont
|
||||
}
|
||||
|
||||
open func calculateLineHeight(for font: UIFont) -> CGFloat {
|
||||
// By default height of the line is equal to 75% of font's size
|
||||
return (font?.pointSize ?? 1) * 0.75
|
||||
font.pointSize * 0.75
|
||||
}
|
||||
|
||||
private func getLineSpacing() -> CGFloat {
|
||||
if let lineSpacing = lineSpacing?(font) {
|
||||
return lineSpacing
|
||||
}
|
||||
|
||||
return font?.xHeight ?? .zero
|
||||
open func calculateLineSpacing(for font: UIFont) -> CGFloat {
|
||||
font.xHeight
|
||||
}
|
||||
|
||||
private func getNumberOfLines() -> Int {
|
||||
guard isMultiline else {
|
||||
return 1
|
||||
}
|
||||
open func copyWith(numberOfLines: Int? = nil,
|
||||
linesWidthFraction: [CGFloat]? = nil,
|
||||
lineHeight: CGFloat? = nil,
|
||||
lineSpacing: CGFloat? = nil,
|
||||
padding: UIEdgeInsets? = nil,
|
||||
maxWidth: CGFloat? = nil,
|
||||
shape: Shape? = nil) -> TextSkeletonsConfiguration {
|
||||
|
||||
if let numberOfLines = numberOfLines {
|
||||
return numberOfLines
|
||||
}
|
||||
|
||||
return labelNumberOfLines == .zero
|
||||
? Constants.defaultNumberOfLines
|
||||
: labelNumberOfLines
|
||||
TextSkeletonsConfiguration(numberOfLines: numberOfLines ?? self.numberOfLines,
|
||||
linesWidthFraction: linesWidthFraction ?? self.linesWidthFraction,
|
||||
lineHeight: lineHeight ?? self.lineHeight,
|
||||
lineSpacing: lineSpacing ?? self.lineSpacing,
|
||||
padding: padding ?? self.padding,
|
||||
maxWidth: maxWidth ?? self.maxWidth,
|
||||
shape: shape ?? self.shape)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,4 +37,13 @@ extension CGRect {
|
|||
|
||||
return CGRect(origin: origin, size: reducedSize)
|
||||
}
|
||||
|
||||
func with(maxWidth: CGFloat) -> CGRect {
|
||||
guard maxWidth.isFinite else {
|
||||
return self
|
||||
}
|
||||
|
||||
return CGRect(origin: origin, size: CGSize(width: min(width, maxWidth),
|
||||
height: height))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -188,31 +188,35 @@ open class SkeletonLayer: CAShapeLayer {
|
|||
|
||||
switch viewType {
|
||||
case let .textView(textView):
|
||||
path = configuration.labelConfiguration.configureTextViewPath(textView: textView)
|
||||
let textViewConfig = configuration.labelConfiguration.createConfiguration(for: textView)
|
||||
|
||||
path = textViewConfig.createPath(for: textView.bounds)
|
||||
|
||||
let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size)
|
||||
frame = configuration.labelConfiguration.applyPadding(viewFrame: viewFrame)
|
||||
frame = textViewConfig.applyPadding(viewFrame: viewFrame)
|
||||
|
||||
case let .label(label):
|
||||
path = configuration.labelConfiguration.configureLabelPath(label: label)
|
||||
let labelConfig = configuration.labelConfiguration.createConfiguration(for: label)
|
||||
|
||||
path = labelConfig.createPath(for: label.bounds)
|
||||
|
||||
let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size)
|
||||
frame = configuration.labelConfiguration.applyPadding(viewFrame: viewFrame)
|
||||
frame = labelConfig.applyPadding(viewFrame: viewFrame)
|
||||
|
||||
case .imageView(_):
|
||||
path = configuration.imageViewConfiguration.drawPath(rect: viewType.view.bounds)
|
||||
path = configuration.imageViewConfiguration.createPath(for: viewType.view.bounds)
|
||||
|
||||
let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size)
|
||||
frame = configuration.imageViewConfiguration.applyPadding(viewFrame: viewFrame)
|
||||
|
||||
case .parentView(_):
|
||||
path = configuration.containerViewConfiguration.drawPath(rect: viewType.view.bounds)
|
||||
path = configuration.containerViewConfiguration.createPath(for: viewType.view.bounds)
|
||||
|
||||
let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size)
|
||||
frame = configuration.containerViewConfiguration.applyPadding(viewFrame: viewFrame)
|
||||
|
||||
case .leafView(_):
|
||||
path = configuration.viewConfiguration.drawPath(rect: viewType.view.bounds)
|
||||
path = configuration.viewConfiguration.createPath(for: viewType.view.bounds)
|
||||
|
||||
let viewFrame = CGRect(origin: rect.origin, size: path?.boundingBox.size ?? rect.size)
|
||||
frame = configuration.viewConfiguration.applyPadding(viewFrame: viewFrame)
|
||||
|
|
|
|||
|
|
@ -77,22 +77,29 @@ public extension UIView {
|
|||
withConfiguration conf: SkeletonsConfiguration,
|
||||
forceNoContainers: Bool = false) -> [SkeletonLayer] {
|
||||
|
||||
let skeletonLayer = conf.createSkeletonLayer(for: self)
|
||||
var subviewSkeletonLayers = [SkeletonLayer]()
|
||||
let config = (view as? SkeletonsPresenter)?.skeletonsConfiguration ?? conf
|
||||
|
||||
if view.isSkeletonsContainer {
|
||||
if !conf.isContainersHidden, !forceNoContainers {
|
||||
let skeletonLayer = config.createSkeletonLayer(for: self)
|
||||
skeletonLayer.bind(to: .parentView(view))
|
||||
|
||||
return [skeletonLayer] + getSkeletonLayer(forView: view,
|
||||
withConfiguration: config,
|
||||
forceNoContainers: true)
|
||||
} else {
|
||||
return view.skeletonableViews
|
||||
.map { getSkeletonLayer(forView: $0,
|
||||
withConfiguration: config,
|
||||
forceNoContainers: true) }
|
||||
.flatMap { $0 }
|
||||
}
|
||||
|
||||
subviewSkeletonLayers = view.skeletonableViews
|
||||
.map { getSkeletonLayer(forView: $0, withConfiguration: conf, forceNoContainers: true) }
|
||||
.flatMap { $0 }
|
||||
} else {
|
||||
let skeletonLayer = config.createSkeletonLayer(for: self)
|
||||
skeletonLayer.bind(to: view.viewType)
|
||||
}
|
||||
|
||||
return [skeletonLayer] + subviewSkeletonLayers
|
||||
return [skeletonLayer]
|
||||
}
|
||||
}
|
||||
|
||||
private func configureBaseLayer(withConfiguration conf: SkeletonsConfiguration) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIElements'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Bunch of useful protocols and views.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIUIKitCore'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Core UI elements: protocols, views and helpers.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIWebView'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Universal web view API'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Pod::Spec.new do |s|
|
||||
s.name = 'TIYandexMapUtils'
|
||||
s.version = '1.53.3'
|
||||
s.version = '1.54.0'
|
||||
s.summary = 'Set of helpers for map objects clustering and interacting using Yandex Maps SDK.'
|
||||
s.homepage = 'https://git.svc.touchin.ru/TouchInstinct/LeadKit/src/tag/' + s.version.to_s + '/' + s.name
|
||||
s.license = { :type => 'MIT', :file => 'LICENSE' }
|
||||
|
|
|
|||
Loading…
Reference in New Issue