diff --git a/Examples/SkeletonUI-macOS-iOS/SkeletonUI-macOS-iOS/Views/CharacterView.swift b/Examples/SkeletonUI-macOS-iOS/SkeletonUI-macOS-iOS/Views/CharacterView.swift index 9820dcb..fb0b9c7 100644 --- a/Examples/SkeletonUI-macOS-iOS/SkeletonUI-macOS-iOS/Views/CharacterView.swift +++ b/Examples/SkeletonUI-macOS-iOS/SkeletonUI-macOS-iOS/Views/CharacterView.swift @@ -25,7 +25,7 @@ struct CharacterView: View { Text(character?.name) .skeleton(with: loading) .shape(type: .capsule) - .multiline(lines: 3, scales: [1: 0.5, 2: 0.25]) + .multiline(lines: 3, scales: [1: 0.5, 2: 0.25], padding: EdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 0)) .appearance(type: .gradient()) .animation(type: .linear()) } @@ -35,7 +35,7 @@ struct CharacterView: View { #if DEBUG struct CharacterView_Previews: PreviewProvider { static var previews: some View { - CharacterView(character: nil, loading: false) + CharacterView(character: nil, loading: true).previewLayout(.sizeThatFits).frame(width: 300, height: 100, alignment: .center) } } #endif diff --git a/Examples/SkeletonUI-watchOS/SkeletonUI-watchOS.xcodeproj/xcshareddata/xcschemes/SkeletonUI-watchOS.xcscheme b/Examples/SkeletonUI-watchOS/SkeletonUI-watchOS.xcodeproj/xcshareddata/xcschemes/SkeletonUI-watchOS.xcscheme index 4a699fc..49ce619 100644 --- a/Examples/SkeletonUI-watchOS/SkeletonUI-watchOS.xcodeproj/xcshareddata/xcschemes/SkeletonUI-watchOS.xcscheme +++ b/Examples/SkeletonUI-watchOS/SkeletonUI-watchOS.xcodeproj/xcshareddata/xcschemes/SkeletonUI-watchOS.xcscheme @@ -54,10 +54,8 @@ debugDocumentVersioning = "YES" debugServiceExtension = "internal" allowLocationSimulation = "YES"> - + - + - + - - - - - + diff --git a/Package.resolved b/Package.resolved index dc5a1db..f3cc78c 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing", "state": { "branch": null, - "revision": "12c6a7ce9d67f39a23c6bab757bdb073bd997885", - "version": "1.7.1" + "revision": "f8a9c997c3c1dab4e216a8ec9014e23144cbab37", + "version": "1.9.0" } } ] diff --git a/SkeletonUI.podspec b/SkeletonUI.podspec index 3c8f777..d23ac9c 100644 --- a/SkeletonUI.podspec +++ b/SkeletonUI.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SkeletonUI' - s.version = '1.0.5' + s.version = '1.0.6' s.summary = 'Elegant skeleton loading animation in SwiftUI and Combine' s.description = <<-DESC SkeletonUI aims to bring an elegant, declarative syntax to skeleton loading animations. Get rid of loading screens or spinners and start using skeletons to represent final content shapes diff --git a/Sources/SkeletonUI/Extensions/ModifiedContent+SkeletonModifier.swift b/Sources/SkeletonUI/Extensions/ModifiedContent+SkeletonModifier.swift index 7031a1e..76b8d7a 100644 --- a/Sources/SkeletonUI/Extensions/ModifiedContent+SkeletonModifier.swift +++ b/Sources/SkeletonUI/Extensions/ModifiedContent+SkeletonModifier.swift @@ -12,10 +12,11 @@ public extension ModifiedContent where Content: View, Modifier == SkeletonModifi return self } - func multiline(lines: Int, scales: [Int: CGFloat]? = nil, spacing: CGFloat? = nil) -> ModifiedContent { + func multiline(lines: Int, scales: [Int: CGFloat]? = nil, spacing: CGFloat? = nil, padding: EdgeInsets? = nil) -> ModifiedContent { modifier.skeleton.multiline.lines.send(lines) modifier.skeleton.multiline.scales.send(scales) modifier.skeleton.multiline.spacing.send(spacing) + modifier.skeleton.multiline.padding.send(padding) return self } diff --git a/Sources/SkeletonUI/Extensions/View+SkeletonModifier.swift b/Sources/SkeletonUI/Extensions/View+SkeletonModifier.swift index 222477e..c4c8ca2 100644 --- a/Sources/SkeletonUI/Extensions/View+SkeletonModifier.swift +++ b/Sources/SkeletonUI/Extensions/View+SkeletonModifier.swift @@ -2,7 +2,7 @@ import SwiftUI @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) public extension View { - func skeleton(with loading: Bool, transition: AnyTransition? = nil, animated: Animation? = nil) -> ModifiedContent { - modifier(SkeletonModifier(skeleton: SkeletonInteractor(loading, transition: transition, animated: animated))) + func skeleton(with loading: Bool, transition: AnyTransition? = nil, animated: Animation? = nil, width: CGFloat? = nil, height: CGFloat? = nil) -> ModifiedContent { + modifier(SkeletonModifier(skeleton: SkeletonInteractor(loading, transition: transition, animated: animated, width: width, height: height))) } } diff --git a/Sources/SkeletonUI/Modifiers/SkeletonModifier.swift b/Sources/SkeletonUI/Modifiers/SkeletonModifier.swift index 6c9ca47..a7af06d 100644 --- a/Sources/SkeletonUI/Modifiers/SkeletonModifier.swift +++ b/Sources/SkeletonUI/Modifiers/SkeletonModifier.swift @@ -31,6 +31,8 @@ public struct SkeletonModifier: ViewModifier { } } } + .padding(skeleton.multiline.presenter.padding ?? EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)) + .frame(width: skeleton.presenter.width, height: skeleton.presenter.height) .transition(skeleton.presenter.transition) } else { content diff --git a/Sources/SkeletonUI/Multiline/MultilineInteractor.swift b/Sources/SkeletonUI/Multiline/MultilineInteractor.swift index 0a7377b..b22e406 100644 --- a/Sources/SkeletonUI/Multiline/MultilineInteractor.swift +++ b/Sources/SkeletonUI/Multiline/MultilineInteractor.swift @@ -9,6 +9,7 @@ protocol MultilineInteractable: AnyObject { var scale: CurrentValueSubject { get } var spacing: CurrentValueSubject { get } var scales: CurrentValueSubject<[Int: CGFloat]?, Never> { get } + var padding: CurrentValueSubject { get } } final class MultilineInteractor: MultilineInteractable { @@ -18,6 +19,7 @@ final class MultilineInteractor: MultilineInteractable { let scale: CurrentValueSubject let spacing: CurrentValueSubject let scales: CurrentValueSubject<[Int: CGFloat]?, Never> + let padding: CurrentValueSubject private var cancellables = Set() @@ -27,6 +29,8 @@ final class MultilineInteractor: MultilineInteractable { scale = CurrentValueSubject(presenter.scale) spacing = CurrentValueSubject(presenter.spacing) scales = CurrentValueSubject<[Int: CGFloat]?, Never>(presenter.scales) + padding = CurrentValueSubject(presenter.padding) + line.map { [weak self] line in guard let self = self else { fatalError() } if let scale = self.scales.value?[line] { @@ -37,5 +41,6 @@ final class MultilineInteractor: MultilineInteractable { lines.assign(to: \.lines, on: presenter).store(in: &cancellables) scales.assign(to: \.scales, on: presenter).store(in: &cancellables) spacing.assign(to: \.spacing, on: presenter).store(in: &cancellables) + padding.assign(to: \.padding, on: presenter).store(in: &cancellables) } } diff --git a/Sources/SkeletonUI/Multiline/MultilinePresenter.swift b/Sources/SkeletonUI/Multiline/MultilinePresenter.swift index 4ba0891..486ff81 100644 --- a/Sources/SkeletonUI/Multiline/MultilinePresenter.swift +++ b/Sources/SkeletonUI/Multiline/MultilinePresenter.swift @@ -6,4 +6,5 @@ final class MultilinePresenter: ObservableObject { @Published var spacing: CGFloat? @Published var scale: CGFloat = 1 @Published var scales: [Int: CGFloat]? + @Published var padding: EdgeInsets? } diff --git a/Sources/SkeletonUI/Skeleton/SkeletonInteractor.swift b/Sources/SkeletonUI/Skeleton/SkeletonInteractor.swift index 3252f2c..ee46409 100644 --- a/Sources/SkeletonUI/Skeleton/SkeletonInteractor.swift +++ b/Sources/SkeletonUI/Skeleton/SkeletonInteractor.swift @@ -17,8 +17,8 @@ final class SkeletonInteractor: SkeletonInteractable { let appearance: AppearanceInteractable let animation: AnimationInteractable - init(_ loading: Bool, transition: AnyTransition?, animated: Animation?, shape: ShapeInteractable = ShapeInteractor(), multiline: MultilineInteractable = MultilineInteractor(), appearance: AppearanceInteractable = AppearanceInteractor(), animation: AnimationInteractable = AnimationInteractor()) { - presenter = SkeletonPresenter(loading, transition: transition, animated: animated) + init(_ loading: Bool, transition: AnyTransition?, animated: Animation?, width: CGFloat?, height: CGFloat?, shape: ShapeInteractable = ShapeInteractor(), multiline: MultilineInteractable = MultilineInteractor(), appearance: AppearanceInteractable = AppearanceInteractor(), animation: AnimationInteractable = AnimationInteractor()) { + presenter = SkeletonPresenter(loading, transition: transition, animated: animated, width: width, height: height) self.shape = shape self.multiline = multiline self.appearance = appearance diff --git a/Sources/SkeletonUI/Skeleton/SkeletonPresenter.swift b/Sources/SkeletonUI/Skeleton/SkeletonPresenter.swift index 647fdb8..03f8b3d 100644 --- a/Sources/SkeletonUI/Skeleton/SkeletonPresenter.swift +++ b/Sources/SkeletonUI/Skeleton/SkeletonPresenter.swift @@ -4,10 +4,14 @@ final class SkeletonPresenter: ObservableObject { @Published var loading: Bool @Published var transition: AnyTransition @Published var animated: Animation + @Published var width: CGFloat? + @Published var height: CGFloat? - init(_ loading: Bool, transition: AnyTransition?, animated: Animation?) { + init(_ loading: Bool, transition: AnyTransition?, animated: Animation?, width: CGFloat?, height: CGFloat?) { self.loading = loading self.transition = transition ?? .opacity self.animated = animated ?? .default + self.width = width + self.height = height } } diff --git a/Tests/SkeletonUISnapshotTests/AutoMockable.generated.swift b/Tests/SkeletonUISnapshotTests/AutoMockable.generated.swift index ddba87c..aed5bd6 100644 --- a/Tests/SkeletonUISnapshotTests/AutoMockable.generated.swift +++ b/Tests/SkeletonUISnapshotTests/AutoMockable.generated.swift @@ -93,6 +93,12 @@ class AppearanceInteractableMock: AppearanceInteractable { } class MultilineInteractableMock: MultilineInteractable { + var underlyingPadding: CurrentValueSubject! + var padding: CurrentValueSubject { + get { return underlyingPadding } + set(value) { underlyingPadding = value } + } + var presenter: MultilinePresenter { get { return underlyingPresenter } set(value) { underlyingPresenter = value } diff --git a/Tests/SkeletonUISnapshotTests/SnapshotTests.swift b/Tests/SkeletonUISnapshotTests/SnapshotTests.swift index 573cb50..c4627bd 100644 --- a/Tests/SkeletonUISnapshotTests/SnapshotTests.swift +++ b/Tests/SkeletonUISnapshotTests/SnapshotTests.swift @@ -22,6 +22,11 @@ final class SnapshotTests: XCTestCase { let view = Text(nil).skeleton(with: true).appearance(type: .solid()).shape(type: .rectangle).multiline(lines: 2, scales: [1: 0.5]).animation(type: .pulse()) assertNamedSnapshot(matching: view, as: .image(size: CGSize(width: 100, height: 50))) } + + func testCustomTextWithPadding() { + let view = Text(nil).skeleton(with: true).appearance(type: .solid()).shape(type: .rectangle).multiline(lines: 2, scales: [1: 0.5], padding: EdgeInsets(top: 5, leading: 0, bottom: 5, trailing: 0)).animation(type: .pulse()) + assertNamedSnapshot(matching: view, as: .image(size: CGSize(width: 100, height: 60))) + } func testDefaultImage() { #if os(macOS)