Skip to content

Commit 46b564a

Browse files
authored
Merge branch 'SDWebImage:master' into master
2 parents 4e069ee + 02b2579 commit 46b564a

File tree

14 files changed

+132
-140
lines changed

14 files changed

+132
-140
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [3.1.0] - 2024-06-27
10+
- Re-implements the aspectRatio support on AnimatedImage, fix issue like cornerRadius #324
11+
- Add Image scale support in WebImage init #323
12+
- Update platform names in `available` attributes #321
13+
- - This is source compatible but binary incompatible version
14+
915
## [3.0.4] - 2024-04-30
1016
- Trying to move the initial state setup before onAppear to fix the watchOS switching url or any other state issue #316
1117
- This solve a issue in history when sometimes SwiftUI does not trigger the `onAppear` and cause state error, like #312 #314

Example/SDWebImageSwiftUIDemo/ContentView.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ class UserSettings: ObservableObject {
1717
#endif
1818
}
1919

20+
#if !os(watchOS)
21+
struct ContentView4: View {
22+
var url = URL(string: "https://github.com/SDWebImage/SDWebImageSwiftUI/assets/97430818/72d27f90-e9d8-48d7-b144-82ada828a027")!
23+
var body: some View {
24+
AnimatedImage(url: url)
25+
.resizable()
26+
.scaledToFit()
27+
// .aspectRatio(nil, contentMode: .fit)
28+
.clipShape(RoundedRectangle(cornerRadius: 50, style: .continuous))
29+
}
30+
}
31+
#endif
32+
2033
// Test Switching nil url
2134
struct ContentView3: View {
2235
@State var isOn = false

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ For downstream framework author, you should create a `Package.swift` file into y
117117
```swift
118118
let package = Package(
119119
dependencies: [
120-
.package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "2.0.0")
120+
.package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "3.0.0")
121121
],
122122
)
123123
```
@@ -655,7 +655,7 @@ class ViewController: UIViewController {
655655
}
656656

657657
// ContentView.swift
658-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
658+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
659659
struct ContentView : View {
660660
var body: some View {
661661
Group {

SDWebImageSwiftUI.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
Pod::Spec.new do |s|
1010
s.name = 'SDWebImageSwiftUI'
11-
s.version = '3.0.4'
11+
s.version = '3.1.0'
1212
s.summary = 'SwiftUI Image loading and Animation framework powered by SDWebImage'
1313

1414
s.description = <<-DESC

SDWebImageSwiftUI/Classes/AnimatedImage.swift

Lines changed: 44 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import SDWebImage
1212
#if !os(watchOS)
1313

1414
/// A coordinator object used for `AnimatedImage`native view bridge for UIKit/AppKit.
15-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
15+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
1616
public final class AnimatedImageCoordinator: NSObject {
1717

1818
/// Any user-provided object for actual coordinator, such as delegate method, taget-action
@@ -25,7 +25,7 @@ public final class AnimatedImageCoordinator: NSObject {
2525
}
2626

2727
/// Data Binding Object, only properties in this object can support changes from user with @State and refresh
28-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
28+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
2929
final class AnimatedImageModel : ObservableObject {
3030
enum Kind {
3131
case url
@@ -53,7 +53,7 @@ final class AnimatedImageModel : ObservableObject {
5353
}
5454

5555
/// Loading Binding Object, only properties in this object can support changes from user with @State and refresh
56-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
56+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
5757
final class AnimatedLoadingModel : ObservableObject {
5858
@Published var image: PlatformImage? // loaded image, note when progressive loading, this will published multiple times with different partial image
5959
@Published var isLoading: Bool = false // whether network is loading or cache is querying, should only be used for indicator binding
@@ -66,7 +66,7 @@ final class AnimatedLoadingModel : ObservableObject {
6666
}
6767

6868
/// Completion Handler Binding Object, supports dynamic @State changes
69-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
69+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
7070
final class AnimatedImageHandler: ObservableObject {
7171
// Completion Handler
7272
@Published var successBlock: ((PlatformImage, Data?, SDImageCacheType) -> Void)?
@@ -78,7 +78,7 @@ final class AnimatedImageHandler: ObservableObject {
7878
}
7979

8080
/// Layout Binding Object, supports dynamic @State changes
81-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
81+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
8282
final class AnimatedImageLayout : ObservableObject {
8383
var contentMode: ContentMode?
8484
var aspectRatio: CGFloat?
@@ -90,7 +90,7 @@ final class AnimatedImageLayout : ObservableObject {
9090
}
9191

9292
/// Configuration Binding Object, supports dynamic @State changes
93-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
93+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
9494
final class AnimatedImageConfiguration: ObservableObject {
9595
var incrementalLoad: Bool?
9696
var maxBufferSize: UInt?
@@ -106,7 +106,7 @@ final class AnimatedImageConfiguration: ObservableObject {
106106
}
107107

108108
/// A Image View type to load image from url, data or bundle. Supports animated and static image format.
109-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
109+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
110110
public struct AnimatedImage : PlatformViewRepresentable {
111111
@ObservedObject var imageModel: AnimatedImageModel
112112
@ObservedObject var imageHandler = AnimatedImageHandler()
@@ -275,6 +275,8 @@ public struct AnimatedImage : PlatformViewRepresentable {
275275
self.imageModel.placeholderView?.isHidden = false
276276
self.imageHandler.failureBlock?(error ?? NSError())
277277
}
278+
// Finished loading, async
279+
finishUpdateView(view, context: context, image: image)
278280
}
279281
}
280282

@@ -361,22 +363,9 @@ public struct AnimatedImage : PlatformViewRepresentable {
361363
break // impossible
362364
}
363365

364-
#if os(macOS)
365-
if self.isAnimating != view.wrapped.animates {
366-
view.wrapped.animates = self.isAnimating
367-
}
368-
#else
369-
if self.isAnimating != view.wrapped.isAnimating {
370-
if self.isAnimating {
371-
view.wrapped.startAnimating()
372-
} else {
373-
view.wrapped.stopAnimating()
374-
}
375-
}
376-
#endif
366+
// Finished loading, sync
367+
finishUpdateView(view, context: context, image: view.wrapped.image)
377368

378-
configureView(view, context: context)
379-
layoutView(view, context: context)
380369
if let viewUpdateBlock = imageHandler.viewUpdateBlock {
381370
viewUpdateBlock(view.wrapped, context)
382371
}
@@ -394,6 +383,17 @@ public struct AnimatedImage : PlatformViewRepresentable {
394383
}
395384
}
396385

386+
func finishUpdateView(_ view: AnimatedImageViewWrapper, context: Context, image: PlatformImage?) {
387+
// Finished loading
388+
if let imageSize = image?.size {
389+
view.imageSize = imageSize
390+
} else {
391+
view.imageSize = nil
392+
}
393+
configureView(view, context: context)
394+
layoutView(view, context: context)
395+
}
396+
397397
func layoutView(_ view: AnimatedImageViewWrapper, context: Context) {
398398
// AspectRatio && ContentMode
399399
#if os(macOS)
@@ -442,9 +442,7 @@ public struct AnimatedImage : PlatformViewRepresentable {
442442
#endif
443443

444444
// Resizable
445-
if let _ = imageLayout.resizingMode {
446-
view.resizable = true
447-
}
445+
view.resizingMode = imageLayout.resizingMode
448446

449447
// Animated Image does not support resizing mode and rendering mode
450448
if let image = view.wrapped.image {
@@ -587,11 +585,26 @@ public struct AnimatedImage : PlatformViewRepresentable {
587585
} else {
588586
view.wrapped.playbackMode = .normal
589587
}
588+
589+
// Animation
590+
#if os(macOS)
591+
if self.isAnimating != view.wrapped.animates {
592+
view.wrapped.animates = self.isAnimating
593+
}
594+
#else
595+
if self.isAnimating != view.wrapped.isAnimating {
596+
if self.isAnimating {
597+
view.wrapped.startAnimating()
598+
} else {
599+
view.wrapped.stopAnimating()
600+
}
601+
}
602+
#endif
590603
}
591604
}
592605

593606
// Layout
594-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
607+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
595608
extension AnimatedImage {
596609

597610
/// Configurate this view's image with the specified cap insets and options.
@@ -630,70 +643,8 @@ extension AnimatedImage {
630643
}
631644
}
632645

633-
// Aspect Ratio
634-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
635-
extension AnimatedImage {
636-
func setImageLayoutAspectRatio(_ aspectRatio: CGFloat?, contentMode: ContentMode) {
637-
self.imageLayout.aspectRatio = aspectRatio
638-
self.imageLayout.contentMode = contentMode
639-
}
640-
641-
/// Constrains this view's dimensions to the specified aspect ratio.
642-
/// - Parameters:
643-
/// - aspectRatio: The ratio of width to height to use for the resulting
644-
/// view. If `aspectRatio` is `nil`, the resulting view maintains this
645-
/// view's aspect ratio.
646-
/// - contentMode: A flag indicating whether this view should fit or
647-
/// fill the parent context.
648-
/// - Returns: A view that constrains this view's dimensions to
649-
/// `aspectRatio`, using `contentMode` as its scaling algorithm.
650-
@ViewBuilder
651-
public func aspectRatio(_ aspectRatio: CGFloat? = nil, contentMode: ContentMode) -> some View {
652-
// The `SwifUI.View.aspectRatio(_:contentMode:)` says:
653-
// If `aspectRatio` is `nil`, the resulting view maintains this view's aspect ratio
654-
// But 1: there are no public API to declare what `this view's aspect ratio` is
655-
// So, if we don't override this method, SwiftUI ignore the content mode on actual ImageView
656-
// To workaround, we want to call the default `SwifUI.View.aspectRatio(_:contentMode:)` method
657-
// But 2: there are no way to call a Protocol Extention default implementation in Swift 5.1
658-
// So, we directly call the implementation detail modifier instead
659-
// Fired Radar: FB7413534
660-
let _ = self.setImageLayoutAspectRatio(aspectRatio, contentMode: contentMode)
661-
if let aspectRatio {
662-
self.modifier(_AspectRatioLayout(aspectRatio: aspectRatio, contentMode: contentMode))
663-
} else {
664-
self
665-
}
666-
}
667-
668-
/// Constrains this view's dimensions to the aspect ratio of the given size.
669-
/// - Parameters:
670-
/// - aspectRatio: A size specifying the ratio of width to height to use
671-
/// for the resulting view.
672-
/// - contentMode: A flag indicating whether this view should fit or
673-
/// fill the parent context.
674-
/// - Returns: A view that constrains this view's dimensions to
675-
/// `aspectRatio`, using `contentMode` as its scaling algorithm.
676-
public func aspectRatio(_ aspectRatio: CGSize, contentMode: ContentMode) -> some View {
677-
return self.aspectRatio(aspectRatio.width / aspectRatio.height, contentMode: contentMode)
678-
}
679-
680-
/// Scales this view to fit its parent.
681-
/// - Returns: A view that scales this view to fit its parent,
682-
/// maintaining this view's aspect ratio.
683-
public func scaledToFit() -> some View {
684-
return self.aspectRatio(nil, contentMode: .fit)
685-
}
686-
687-
/// Scales this view to fill its parent.
688-
/// - Returns: A view that scales this view to fit its parent,
689-
/// maintaining this view's aspect ratio.
690-
public func scaledToFill() -> some View {
691-
return self.aspectRatio(nil, contentMode: .fill)
692-
}
693-
}
694-
695646
// AnimatedImage Modifier
696-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
647+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
697648
extension AnimatedImage {
698649

699650
/// Total loop count for animated image rendering. Defaults to nil.
@@ -770,7 +721,7 @@ extension AnimatedImage {
770721
}
771722

772723
// Completion Handler
773-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
724+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
774725
extension AnimatedImage {
775726

776727
/// Provide the action when image load fails.
@@ -802,7 +753,7 @@ extension AnimatedImage {
802753
}
803754

804755
// View Coordinator Handler
805-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
756+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
806757
extension AnimatedImage {
807758

808759
/// Provide the action when view representable create the native view.
@@ -839,7 +790,7 @@ extension SDWebImageIndicator where Self == SDWebImageProgressIndicator {
839790
}
840791

841792
// Web Image convenience, based on UIKit/AppKit API
842-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
793+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
843794
extension AnimatedImage {
844795

845796
/// Associate a indicator when loading image with url
@@ -860,7 +811,7 @@ extension AnimatedImage {
860811
}
861812

862813
#if DEBUG
863-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
814+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
864815
struct AnimatedImage_Previews : PreviewProvider {
865816
static var previews: some View {
866817
Group {

SDWebImageSwiftUI/Classes/Image.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import Foundation
1010
import SwiftUI
1111

12-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
12+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
1313
extension Image {
1414
@inlinable init(platformImage: PlatformImage) {
1515
#if os(macOS)
@@ -20,13 +20,13 @@ extension Image {
2020
}
2121
}
2222

23-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
23+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
2424
extension PlatformImage {
2525
static var empty = PlatformImage()
2626
}
2727

2828
#if !os(macOS)
29-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
29+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
3030
extension PlatformImage.Orientation {
3131
@inlinable var toSwiftUI: Image.Orientation {
3232
switch self {
@@ -52,7 +52,7 @@ extension PlatformImage.Orientation {
5252
}
5353
}
5454

55-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
55+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
5656
extension Image.Orientation {
5757
@inlinable var toPlatform: PlatformImage.Orientation {
5858
switch self {

SDWebImageSwiftUI/Classes/ImageManager.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import SDWebImage
1212

1313
/// A Image observable object for handle image load process. This drive the Source of Truth for image loading status.
1414
/// You can use `@ObservedObject` to associate each instance of manager to your View type, which update your view's body from SwiftUI framework when image was loaded.
15-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
15+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
1616
public final class ImageManager : ObservableObject {
1717
/// loaded image, note when progressive loading, this will published multiple times with different partial image
1818
public var image: PlatformImage? {
@@ -136,7 +136,7 @@ public final class ImageManager : ObservableObject {
136136
}
137137

138138
// Completion Handler
139-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
139+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
140140
extension ImageManager {
141141
/// Provide the action when image load fails.
142142
/// - Parameters:

SDWebImageSwiftUI/Classes/ImagePlayer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import Combine
1111
import SDWebImage
1212

1313
/// A Image observable object for handle aniamted image playback. This is used to avoid `@State` update may capture the View struct type and cause memory leak.
14-
@available(iOS 14.0, OSX 11.0, tvOS 14.0, watchOS 7.0, *)
14+
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
1515
public final class ImagePlayer : ObservableObject {
1616
var player: SDAnimatedImagePlayer?
1717

0 commit comments

Comments
 (0)