From ba33e27b14840d8143be35931af3e1abede45c8d Mon Sep 17 00:00:00 2001 From: onevcat Date: Wed, 25 Oct 2023 13:52:58 +0900 Subject: [PATCH] Update docs for SwiftUI --- Sources/Extensions/ImageView+Kingfisher.swift | 189 +++++++++--------- Sources/Extensions/NSButton+Kingfisher.swift | 68 +++---- .../NSTextAttachment+Kingfisher.swift | 71 ++++--- Sources/Image/ImageDrawing.swift | 2 +- Sources/SwiftUI/KFAnimatedImage.swift | 6 +- Sources/SwiftUI/KFImage.swift | 44 ++++ Sources/SwiftUI/KFImageOptions.swift | 98 +++++---- Sources/SwiftUI/KFImageProtocol.swift | 35 +++- Sources/Utility/CallbackQueue.swift | 24 +-- Sources/Utility/Delegate.swift | 16 +- Sources/Utility/Result.swift | 2 +- Sources/Utility/SizeExtensions.swift | 25 +-- Sources/Views/AnimatedImageView.swift | 86 ++++---- Sources/Views/Indicator.swift | 55 +++-- 14 files changed, 414 insertions(+), 307 deletions(-) diff --git a/Sources/Extensions/ImageView+Kingfisher.swift b/Sources/Extensions/ImageView+Kingfisher.swift index 9378f192a..28290ff7a 100644 --- a/Sources/Extensions/ImageView+Kingfisher.swift +++ b/Sources/Extensions/ImageView+Kingfisher.swift @@ -36,22 +36,21 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { // MARK: Setting Image - /// Sets an image to the image view with a `Source`. + /// Sets an image to the image view with a ``Source``. /// /// - Parameters: - /// - source: The `Source` object defines data information from network or a data provider. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - source: The ``Source`` object that defines data information from the network or a data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: /// This is the easiest way to use Kingfisher to boost the image setting process from a source. Since all parameters /// have a default value except the `source`, you can set an image from a certain URL to an image view like this: /// - /// ``` + /// ```swift /// // Set image from a network source. /// let url = URL(string: "https://example.com/image.png")! /// imageView.kf.setImage(with: .network(url)) @@ -61,44 +60,44 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { /// imageView.kf.setImage(with: .provider(provider)) /// ``` /// - /// For both `.network` and `.provider` source, there are corresponding view extension methods. So the code - /// above is equivalent to: + /// For both ``Source/network(_:)`` and ``Source/provider(_:)`` sources, there are corresponding view extension + /// methods. So the code above is equivalent to: /// - /// ``` + /// ```swift /// imageView.kf.setImage(with: url) /// imageView.kf.setImage(with: provider) /// ``` /// - /// Internally, this method will use `KingfisherManager` to get the source. - /// Since this method will perform UI changes, you must call it from the main thread. - /// Both `progressBlock` and `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with source: Source?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { let options = KingfisherParsedOptionsInfo(KingfisherManager.shared.defaultOptions + (options ?? .empty)) return setImage(with: source, placeholder: placeholder, parsedOptions: options, progressBlock: progressBlock, completionHandler: completionHandler) } - /// Sets an image to the image view with a `Source`. + /// Sets an image to the image view with a ``Source``. /// /// - Parameters: - /// - source: The `Source` object defines data information from network or a data provider. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - source: The ``Source`` object that defines data information from the network or a data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: /// This is the easiest way to use Kingfisher to boost the image setting process from a source. Since all parameters /// have a default value except the `source`, you can set an image from a certain URL to an image view like this: /// - /// ``` + /// ```swift /// // Set image from a network source. /// let url = URL(string: "https://example.com/image.png")! /// imageView.kf.setImage(with: .network(url)) @@ -108,24 +107,25 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { /// imageView.kf.setImage(with: .provider(provider)) /// ``` /// - /// For both `.network` and `.provider` source, there are corresponding view extension methods. So the code - /// above is equivalent to: + /// For both ``Source/network(_:)`` and ``Source/provider(_:)`` sources, there are corresponding view extension + /// methods. So the code above is equivalent to: /// - /// ``` + /// ```swift /// imageView.kf.setImage(with: url) /// imageView.kf.setImage(with: provider) /// ``` /// - /// Internally, this method will use `KingfisherManager` to get the source. - /// Since this method will perform UI changes, you must call it from the main thread. - /// The `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with source: Source?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { return setImage( with: source, @@ -135,38 +135,39 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { completionHandler: completionHandler ) } - - /// Sets an image to the image view with a requested resource. + + /// Sets an image to the image view with a requested ``Resource``. /// /// - Parameters: - /// - resource: The `Resource` object contains information about the resource. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - resource: The ``Resource`` object contains information about the resource. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: - /// This is the easiest way to use Kingfisher to boost the image setting process from network. Since all parameters - /// have a default value except the `resource`, you can set an image from a certain URL to an image view like this: + /// This is the easiest way to use Kingfisher to boost the image setting process from a source. Since all parameters + /// have a default value except the `source`, you can set an image from a certain URL to an image view like this: /// - /// ``` + /// ```swift + /// // Set image from a URL resource. /// let url = URL(string: "https://example.com/image.png")! /// imageView.kf.setImage(with: url) /// ``` /// - /// Internally, this method will use `KingfisherManager` to get the requested resource, from either cache - /// or network. Since this method will perform UI changes, you must call it from the main thread. - /// Both `progressBlock` and `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with resource: Resource?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { return setImage( with: resource?.convertToSource(), @@ -176,34 +177,35 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { completionHandler: completionHandler) } - /// Sets an image to the image view with a requested resource. + /// Sets an image to the image view with a requested ``Resource``. /// /// - Parameters: - /// - resource: The `Resource` object contains information about the resource. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - resource: The ``Resource`` object contains information about the resource. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: - /// This is the easiest way to use Kingfisher to boost the image setting process from network. Since all parameters - /// have a default value except the `resource`, you can set an image from a certain URL to an image view like this: + /// This is the easiest way to use Kingfisher to boost the image setting process from a source. Since all parameters + /// have a default value except the `source`, you can set an image from a certain URL to an image view like this: /// - /// ``` + /// ```swift + /// // Set image from a URL resource. /// let url = URL(string: "https://example.com/image.png")! /// imageView.kf.setImage(with: url) /// ``` /// - /// Internally, this method will use `KingfisherManager` to get the requested resource, from either cache - /// or network. Since this method will perform UI changes, you must call it from the main thread. - /// The `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with resource: Resource?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { return setImage( with: resource, @@ -214,28 +216,29 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { ) } - /// Sets an image to the image view with a data provider. + /// Sets an image to the image view with a ``ImageDataProvider``. /// /// - Parameters: - /// - provider: The `ImageDataProvider` object contains information about the data. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - provider: The ``ImageDataProvider`` object that defines data information from the data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// Internally, this method will use `KingfisherManager` to get the image data, from either cache - /// or the data provider. Since this method will perform UI changes, you must call it from the main thread. - /// Both `progressBlock` and `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with provider: ImageDataProvider?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, progressBlock: DownloadProgressBlock? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { return setImage( with: provider.map { .provider($0) }, @@ -245,25 +248,26 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { completionHandler: completionHandler) } - /// Sets an image to the image view with a data provider. + /// Sets an image to the image view with a ``ImageDataProvider``. /// /// - Parameters: - /// - provider: The `ImageDataProvider` object contains information about the data. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - provider: The ``ImageDataProvider`` object that defines data information from the data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// Internally, this method will use `KingfisherManager` to get the image data, from either cache - /// or the data provider. Since this method will perform UI changes, you must call it from the main thread. - /// The `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with provider: ImageDataProvider?, placeholder: Placeholder? = nil, options: KingfisherOptionsInfo? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { return setImage( with: provider, @@ -274,13 +278,13 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { ) } - func setImage( with source: Source?, placeholder: Placeholder? = nil, parsedOptions: KingfisherParsedOptionsInfo, progressBlock: DownloadProgressBlock? = nil, - completionHandler: ((Result) -> Void)? = nil) -> DownloadTask? + completionHandler: ((Result) -> Void)? = nil + ) -> DownloadTask? { var mutatingSelf = self guard let source = source else { @@ -367,6 +371,7 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { // MARK: Cancelling Downloading Task /// Cancels the image download task of the image view if it is running. + /// /// Nothing will happen if the downloading has already finished. public func cancelDownloadTask() { imageTask?.cancel() @@ -438,8 +443,9 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { } } - /// Holds which indicator type is going to be used. - /// Default is `.none`, means no indicator will be shown while downloading. + /// Specifies which indicator type is going to be used. + /// + /// The default is ``IndicatorType/none``, which means no indicator will be shown while downloading. public var indicatorType: IndicatorType { get { return getAssociatedObject(base, &indicatorTypeKey) ?? .none @@ -457,9 +463,10 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { } } - /// Holds any type that conforms to the protocol `Indicator`. + /// Holds any type that conforms to the protocol ``Indicator``. + /// /// The protocol `Indicator` has a `view` property that will be shown when loading an image. - /// It will be `nil` if `indicatorType` is `.none`. + /// It will be `nil` if ``KingfisherWrapper/indicatorType`` is ``IndicatorType/none``. public private(set) var indicator: Indicator? { get { let box: Box? = getAssociatedObject(base, &indicatorKey) @@ -510,8 +517,9 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { set { setRetainedAssociatedObject(base, &imageTaskKey, newValue)} } - /// Represents the `Placeholder` used for this image view. A `Placeholder` will be shown in the view while - /// it is downloading an image. + /// Represents the ``Placeholder`` used for this image view. + /// + /// A ``Placeholder`` will be shown in the view while it is downloading an image. public private(set) var placeholder: Placeholder? { get { return getAssociatedObject(base, &placeholderKey) } set { @@ -529,7 +537,6 @@ extension KingfisherWrapper where Base: KFCrossPlatformImageView { } } - extension KFCrossPlatformImageView { @objc func shouldPreloadAllAnimation() -> Bool { return true } } diff --git a/Sources/Extensions/NSButton+Kingfisher.swift b/Sources/Extensions/NSButton+Kingfisher.swift index d33d557d1..c7c830033 100644 --- a/Sources/Extensions/NSButton+Kingfisher.swift +++ b/Sources/Extensions/NSButton+Kingfisher.swift @@ -32,22 +32,21 @@ extension KingfisherWrapper where Base: NSButton { // MARK: Setting Image - /// Sets an image to the button with a source. + /// Sets an image to the button with a ``Source``. /// /// - Parameters: - /// - source: The `Source` object contains information about how to get the image. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - source: The ``Source`` object that defines data information from the network or a data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: - /// Internally, this method will use `KingfisherManager` to get the requested source. - /// Since this method will perform UI changes, you must call it from the main thread. - /// Both `progressBlock` and `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with source: Source?, @@ -66,22 +65,21 @@ extension KingfisherWrapper where Base: NSButton { ) } - /// Sets an image to the button with a requested resource. + /// Sets an image to the button with a ``Resource``. /// /// - Parameters: - /// - resource: The `Resource` object contains information about the resource. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - resource: The ``Resource`` object that defines data information from the network or a data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: - /// Internally, this method will use `KingfisherManager` to get the requested resource, from either cache - /// or network. Since this method will perform UI changes, you must call it from the main thread. - /// Both `progressBlock` and `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setImage( with resource: Resource?, @@ -196,22 +194,21 @@ extension KingfisherWrapper where Base: NSButton { ) } - /// Sets an alternate image to the button with a requested resource. + /// Sets an alternate image to the button with a ``Resource``. /// /// - Parameters: - /// - resource: The `Resource` object contains information about the resource. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - resource: The ``Resource`` object that defines data information from the network or a data provider. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: - /// Internally, this method will use `KingfisherManager` to get the requested resource, from either cache - /// or network. Since this method will perform UI changes, you must call it from the main thread. - /// Both `progressBlock` and `completionHandler` will be also executed in the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the source. Since this method will perform UI + /// changes, it is your responsibility to call it from the main thread. /// + /// > Both `progressBlock` and `completionHandler` will also be executed in the main thread. @discardableResult public func setAlternateImage( with resource: Resource?, @@ -255,7 +252,7 @@ extension KingfisherWrapper where Base: NSButton { options.onDataReceived = (options.onDataReceived ?? []) + [ImageLoadingProgressSideEffect(block)] } - if let provider = ImageProgressiveProvider(options, refresh: { image in + if let provider = ImageProgressiveProvider(options: options, refresh: { image in self.base.alternateImage = image }) { options.onDataReceived = (options.onDataReceived ?? []) + [provider] @@ -308,7 +305,8 @@ extension KingfisherWrapper where Base: NSButton { // MARK: Cancelling Alternate Image Downloading Task - /// Cancels the alternate image download task of the button if it is running. + /// Cancels the image download task of the image view if it is running. + /// /// Nothing will happen if the downloading has already finished. public func cancelAlternateImageDownloadTask() { alternateImageTask?.cancel() diff --git a/Sources/Extensions/NSTextAttachment+Kingfisher.swift b/Sources/Extensions/NSTextAttachment+Kingfisher.swift index 23f627eb8..49a49ee40 100644 --- a/Sources/Extensions/NSTextAttachment+Kingfisher.swift +++ b/Sources/Extensions/NSTextAttachment+Kingfisher.swift @@ -39,33 +39,31 @@ extension KingfisherWrapper where Base: NSTextAttachment { /// Sets an image to the text attachment with a source. /// /// - Parameters: - /// - source: The `Source` object defines data information from network or a data provider. - /// - attributedView: The owner of the attributed string which this `NSTextAttachment` is added. - /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - source: The ``Source`` object that defines data information from the network or a data provider. + /// - attributedView: The owner of the attributed string to which this `NSTextAttachment` is added. + /// - placeholder: A placeholder to show while retrieving the image from the given `source`. + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. - /// - /// - Note: + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// Internally, this method will use `KingfisherManager` to get the requested source - /// Since this method will perform UI changes, you must call it from the main thread. + /// Internally, this method will use ``KingfisherManager`` to get the requested source. Since this method will + /// perform UI changes, it is your responsibility of calling it from the main thread. /// - /// The retrieved image will be set to `NSTextAttachment.image` property. Because it is not an image view based - /// rendering, options related to view, such as `.transition`, are not supported. + /// The retrieved image will be set to the `NSTextAttachment.image` property. Because it is not an image view-based + /// rendering, options related to the view, such as ``KingfisherOptionsInfoItem/transition(_:)``, are not supported. /// - /// Kingfisher will call `setNeedsDisplay` on the `attributedView` when the image task done. It gives the view a + /// Kingfisher will call `setNeedsDisplay` on the `attributedView` when the image task is done. It gives the view a /// chance to render the attributed string again for displaying the downloaded image. For example, if you set an - /// attributed with this `NSTextAttachment` to a `UILabel` object, pass it as the `attributedView` parameter. + /// attributed string with this `NSTextAttachment` to a `UILabel` object, pass it as the `attributedView` parameter. /// /// Here is a typical use case: /// /// ```swift - /// let attributedText = NSMutableAttributedString(string: "Hello World") - /// let textAttachment = NSTextAttachment() + /// let label: UILabel = // ... /// + /// let textAttachment = NSTextAttachment() /// textAttachment.kf.setImage( /// with: URL(string: "https://onevcat.com/assets/images/avatar.jpg")!, /// attributedView: label, @@ -75,10 +73,11 @@ extension KingfisherWrapper where Base: NSTextAttachment { /// |> RoundCornerImageProcessor(cornerRadius: 15)) /// ] /// ) + /// + /// let attributedText = NSMutableAttributedString(string: "Hello World") /// attributedText.replaceCharacters(in: NSRange(), with: NSAttributedString(attachment: textAttachment)) /// label.attributedText = attributedText /// ``` - /// @discardableResult public func setImage( with source: Source?, @@ -102,33 +101,31 @@ extension KingfisherWrapper where Base: NSTextAttachment { /// Sets an image to the text attachment with a source. /// /// - Parameters: - /// - resource: The `Resource` object contains information about the resource. - /// - attributedView: The owner of the attributed string which this `NSTextAttachment` is added. + /// - resource: The ``Resource`` object that defines data information from the network or a data provider. + /// - attributedView: The owner of the attributed string to which this `NSTextAttachment` is added. /// - placeholder: A placeholder to show while retrieving the image from the given `resource`. - /// - options: An options set to define image setting behaviors. See `KingfisherOptionsInfo` for more. - /// - progressBlock: Called when the image downloading progress gets updated. If the response does not contain an + /// - options: A set of options to define image setting behaviors. See ``KingfisherOptionsInfo`` for more. + /// - progressBlock: Called when the image downloading progress is updated. If the response does not contain an /// `expectedContentLength`, this block will not be called. - /// - completionHandler: Called when the image retrieved and set finished. - /// - Returns: A task represents the image downloading. + /// - completionHandler: Called when the image retrieval and setting are finished. + /// - Returns: A task that represents the image downloading. /// - /// - Note: + /// Internally, this method will use ``KingfisherManager`` to get the requested source. Since this method will + /// perform UI changes, it is your responsibility of calling it from the main thread. /// - /// Internally, this method will use `KingfisherManager` to get the requested source - /// Since this method will perform UI changes, you must call it from the main thread. + /// The retrieved image will be set to the `NSTextAttachment.image` property. Because it is not an image view-based + /// rendering, options related to the view, such as ``KingfisherOptionsInfoItem/transition(_:)``, are not supported. /// - /// The retrieved image will be set to `NSTextAttachment.image` property. Because it is not an image view based - /// rendering, options related to view, such as `.transition`, are not supported. - /// - /// Kingfisher will call `setNeedsDisplay` on the `attributedView` when the image task done. It gives the view a + /// Kingfisher will call `setNeedsDisplay` on the `attributedView` when the image task is done. It gives the view a /// chance to render the attributed string again for displaying the downloaded image. For example, if you set an - /// attributed with this `NSTextAttachment` to a `UILabel` object, pass it as the `attributedView` parameter. + /// attributed string with this `NSTextAttachment` to a `UILabel` object, pass it as the `attributedView` parameter. /// /// Here is a typical use case: /// /// ```swift - /// let attributedText = NSMutableAttributedString(string: "Hello World") - /// let textAttachment = NSTextAttachment() + /// let label: UILabel = // ... /// + /// let textAttachment = NSTextAttachment() /// textAttachment.kf.setImage( /// with: URL(string: "https://onevcat.com/assets/images/avatar.jpg")!, /// attributedView: label, @@ -138,10 +135,11 @@ extension KingfisherWrapper where Base: NSTextAttachment { /// |> RoundCornerImageProcessor(cornerRadius: 15)) /// ] /// ) + /// + /// let attributedText = NSMutableAttributedString(string: "Hello World") /// attributedText.replaceCharacters(in: NSRange(), with: NSAttributedString(attachment: textAttachment)) /// label.attributedText = attributedText /// ``` - /// @discardableResult public func setImage( with resource: Resource?, @@ -238,7 +236,8 @@ extension KingfisherWrapper where Base: NSTextAttachment { // MARK: Cancelling Image - /// Cancel the image download task bounded to the text attachment if it is running. + /// Cancel the image download task bound to the text attachment if it is running. + /// /// Nothing will happen if the downloading has already finished. public func cancelDownloadTask() { imageTask?.cancel() diff --git a/Sources/Image/ImageDrawing.swift b/Sources/Image/ImageDrawing.swift index 49838496a..8e2419edb 100644 --- a/Sources/Image/ImageDrawing.swift +++ b/Sources/Image/ImageDrawing.swift @@ -563,7 +563,7 @@ extension KingfisherWrapper where Base: KFCrossPlatformImage { return base } - #if !os(watchOS) + #if !os(watchOS) && !os(macOS) // In newer system versions, use `preparingForDisplay`. if #available(iOS 15.0, tvOS 15.0, visionOS 1.0, *) { if base.scale == scale, let image = base.preparingForDisplay() { diff --git a/Sources/SwiftUI/KFAnimatedImage.swift b/Sources/SwiftUI/KFAnimatedImage.swift index ad25eb232..b2851862c 100644 --- a/Sources/SwiftUI/KFAnimatedImage.swift +++ b/Sources/SwiftUI/KFAnimatedImage.swift @@ -28,7 +28,7 @@ import SwiftUI import Combine -@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) public struct KFAnimatedImage: KFImageProtocol { public typealias HoldingView = KFAnimatedImageViewRepresenter public var context: Context @@ -48,7 +48,7 @@ public struct KFAnimatedImage: KFImageProtocol { } /// A wrapped `UIViewRepresentable` of `AnimatedImageView` -@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) public struct KFAnimatedImageViewRepresenter: UIViewRepresentable, KFImageHoldingView { public typealias RenderingView = AnimatedImageView public static func created(from image: KFCrossPlatformImage?, context: KFImage.Context) -> KFAnimatedImageViewRepresenter { @@ -77,7 +77,7 @@ public struct KFAnimatedImageViewRepresenter: UIViewRepresentable, KFImageHoldin } #if DEBUG -@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) +@available(iOS 14.0, tvOS 14.0, watchOS 7.0, *) struct KFAnimatedImage_Previews: PreviewProvider { static var previews: some View { Group { diff --git a/Sources/SwiftUI/KFImage.swift b/Sources/SwiftUI/KFImage.swift index 67f1c19af..7a4119bfc 100644 --- a/Sources/SwiftUI/KFImage.swift +++ b/Sources/SwiftUI/KFImage.swift @@ -28,9 +28,53 @@ import SwiftUI import Combine +/// Represents an image view in SwiftUI that manages its content using Kingfisher. +/// +/// This view asynchronously loads the content. You can set a ``Source`` to load for the ``KFImage`` through +/// its ``KFImage/init(source:)`` or ``KFImage/init(_:)`` initializers or other relevant methods in ``KF`` Builder type. +/// Kingfisher will first look for the required image in the cache. If it is not found, it will load it via the +/// ``Source`` and provide the result for display, following sending the result to cache and for the future use. +/// +/// When using a `URL` valve as the ``Source``, it is similar to SwiftUI's `AsyncImage` but with additional support +/// for caching. +/// +/// Here is a basic example of using ``KFImage``: +/// +/// ```swift +/// var body: some View { +/// KFImage(URL(string: "https://example.com/image.png")!) +/// } +/// ``` +/// +/// Usually, you can also use the value by calling additional modifiers defined on it, to configure the view: +/// +/// ```swift +/// var body: some View { +/// KFImage.url(url) +/// .placeholder(placeholderImage) +/// .setProcessor(processor) +/// .loadDiskFileSynchronously() +/// .cacheMemoryOnly() +/// .onSuccess { result in } +/// } +/// ``` +/// Here only very few are listed as demonstration. To check other available modifiers, see ``KFOptionSetter`` and its +/// extension methods. +/// @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public struct KFImage: KFImageProtocol { + + /// Represent the wrapping context of the image view. + /// + /// Inside ``KFImage`` it is using the `SwiftUI.Image` to render the image. public var context: Context + + /// Initializes the ``KFImage`` with a context. + /// + /// This should be only used internally in Kingfisher. Do not use this initializer yourself. Instead, use + /// ``KFImage/init(source:)`` or ``KFImage/init(_:)`` initializers or other relevant methods in ``KF`` Builder + /// type. + /// - Parameter context: The context value that the image view should wrap. public init(context: Context) { self.context = context } diff --git a/Sources/SwiftUI/KFImageOptions.swift b/Sources/SwiftUI/KFImageOptions.swift index a63d90976..0a44a2112 100644 --- a/Sources/SwiftUI/KFImageOptions.swift +++ b/Sources/SwiftUI/KFImageOptions.swift @@ -32,10 +32,11 @@ import Combine @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension KFImageProtocol { - /// Creates a `KFImage` for a given `Source`. + /// Creates a Kingfisher-compatible image view with a given ``Source``. + /// /// - Parameters: - /// - source: The `Source` object defines data information from network or a data provider. - /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. + /// - source: The ``Source`` object that defines data information from the network or a data provider. + /// - Returns: A Kingfisher-compatible image view for future configuration or embedding into another `SwiftUI.View`. public static func source( _ source: Source? ) -> Self @@ -43,10 +44,11 @@ extension KFImageProtocol { Self.init(source: source) } - /// Creates a `KFImage` for a given `Resource`. + /// Creates a Kingfisher-compatible image view with a given ``Resource``. + /// /// - Parameters: - /// - source: The `Resource` object defines data information like key or URL. - /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. + /// - resource: The ``Resource`` object that defines data information such as a key or URL. + /// - Returns: A Kingfisher-compatible image view for future configuration or embedding into another `SwiftUI.View`. public static func resource( _ resource: Resource? ) -> Self @@ -54,12 +56,13 @@ extension KFImageProtocol { source(resource?.convertToSource()) } - /// Creates a `KFImage` for a given `URL`. + /// Creates a Kingfisher-compatible image view with a given `URL`. + /// /// - Parameters: - /// - url: The URL where the image should be downloaded. - /// - cacheKey: The key used to store the downloaded image in cache. - /// If `nil`, the `absoluteString` of `url` is used as the cache key. - /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. + /// - url: The `URL` from which the image should be downloaded. + /// - cacheKey: The key used to store the downloaded image in the cache. If `nil`, the `absoluteString` of `url` + /// is used as the cache key. + /// - Returns: A Kingfisher-compatible image view for future configuration or embedding into another `SwiftUI.View`. public static func url( _ url: URL?, cacheKey: String? = nil ) -> Self @@ -67,10 +70,12 @@ extension KFImageProtocol { source(url?.convertToSource(overrideCacheKey: cacheKey)) } - /// Creates a `KFImage` for a given `ImageDataProvider`. + /// Creates a Kingfisher-compatible image view with a given ``ImageDataProvider``. + /// /// - Parameters: - /// - provider: The `ImageDataProvider` object contains information about the data. - /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. + /// - provider: The ``ImageDataProvider`` object that contains information about the data. + /// - Returns: A Kingfisher-compatible image view for future configuration or embedding into another `SwiftUI.View`. + public static func dataProvider( _ provider: ImageDataProvider? ) -> Self @@ -78,11 +83,12 @@ extension KFImageProtocol { source(provider?.convertToSource()) } - /// Creates a builder for some given raw data and a cache key. + /// Creates a builder for the provided raw data and a cache key. + /// /// - Parameters: /// - data: The data object from which the image should be created. - /// - cacheKey: The key used to store the downloaded image in cache. - /// - Returns: A `KFImage` for future configuration or embedding to a `SwiftUI.View`. + /// - cacheKey: The key used to store the downloaded image in the cache. + /// - Returns: A Kingfisher-compatible image view for future configuration or embedding into another `SwiftUI.View`. public static func data( _ data: Data?, cacheKey: String ) -> Self @@ -97,9 +103,11 @@ extension KFImageProtocol { @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) extension KFImageProtocol { - /// Sets a placeholder `View` which shows when loading the image, with a progress parameter as input. - /// - Parameter content: A view that describes the placeholder. - /// - Returns: A `KFImage` view that contains `content` as its placeholder. + + /// Sets a placeholder `View` that is displayed during the image loading, with a progress parameter as input. + /// + /// - Parameter content: A view that represents the placeholder. + /// - Returns: A Kingfisher-compatible image view that includes the provided `content` as its placeholder. public func placeholder(@ViewBuilder _ content: @escaping (Progress) -> P) -> Self { context.placeholder = { progress in return AnyView(content(progress)) @@ -107,29 +115,32 @@ extension KFImageProtocol { return self } - /// Sets a placeholder `View` which shows when loading the image. - /// - Parameter content: A view that describes the placeholder. - /// - Returns: A `KFImage` view that contains `content` as its placeholder. + /// Sets a placeholder `View` that is displayed during the image loading. + /// + /// - Parameter content: A view that represents the placeholder. + /// - Returns: A Kingfisher-compatible image view that includes the provided `content` as its placeholder. public func placeholder(@ViewBuilder _ content: @escaping () -> P) -> Self { placeholder { _ in content() } } - /// Sets cancelling the download task bound to `self` when the view disappearing. - /// - Parameter flag: Whether cancel the task or not. - /// - Returns: A `KFImage` view that cancels downloading task when disappears. + /// Enables canceling the download task associated with `self` when the view disappears. + /// + /// - Parameter flag: A boolean value indicating whether to cancel the task. + /// - Returns: A Kingfisher-compatible image view that cancels the download task when it disappears. public func cancelOnDisappear(_ flag: Bool) -> Self { context.cancelOnDisappear = flag return self } /// Sets a fade transition for the image task. + /// /// - Parameter duration: The duration of the fade transition. - /// - Returns: A `KFImage` with changes applied. + /// - Returns: A Kingfisher-compatible image view with the applied changes. /// - /// Kingfisher will use the fade transition to animate the image in if it is downloaded from web. - /// The transition will not happen when the - /// image is retrieved from either memory or disk cache by default. If you need to do the transition even when - /// the image being retrieved from cache, also call `forceRefresh()` on the returned `KFImage`. + /// Kingfisher will use the fade transition to animate the image if it is downloaded from the web. The transition + /// will not occur when the image is retrieved from either memory or disk cache by default. If you need the + /// transition to occur even when the image is retrieved from the cache, also call + /// ``KFImageProtocol/forceRefresh(_:)`` on the returned view. public func fade(duration: TimeInterval) -> Self { context.options.transition = .fade(duration) return self @@ -137,19 +148,22 @@ extension KFImageProtocol { /// Sets whether to start the image loading before the view actually appears. /// - /// By default, Kingfisher performs a lazy loading for `KFImage`. The image loading won't start until the view's - /// `onAppear` is called. However, sometimes you may want to trigger an aggressive loading for the view. By enabling - /// this, the `KFImage` will try to load the view when its `body` is evaluated when the image loading is not yet - /// started or a previous loading did fail. - /// - /// - Parameter flag: Whether the image loading should happen before view appear. Default is `true`. - /// - Returns: A `KFImage` with changes applied. + /// - Parameter flag: A boolean value indicating whether the image loading should happen before the view appears. The default is `true`. + /// - Returns: A Kingfisher-compatible image view with the applied changes. /// - /// - Note: This is a temporary workaround for an issue from iOS 16, where the SwiftUI view's `onAppear` is not - /// called when it is deeply embedded inside a `List` or `ForEach`. - /// See [#1988](https://github.com/onevcat/Kingfisher/issues/1988). It may cause performance regression, especially - /// if you have a lot of images to load in the view. Use it as your own risk. + /// By default, Kingfisher performs lazy loading for `KFImage`. The image loading won't start until the view's + /// `onAppear` is called. However, sometimes you may want to trigger aggressive loading for the view. By enabling + /// this, the `KFImage` will attempt to load the view when its `body` is evaluated if the image loading has not + /// yet started or if a previous loading attempt failed. /// + /// > Important: This was a temporary workaround for an issue that arose in iOS 16, where the SwiftUI view's + /// > `onAppear` was not called when it was deeply embedded inside a `List` or `ForEach`. This is no longer necessary + /// > if built with Xcode 14.3 and deployed to iOS 16.4 or later. So, it is not needed anymore. + /// > + /// > Enabling this may cause performance regression, especially if you have a lot of images to load in the view. + /// > Use it at your own risk. + /// > + /// > Please refer to [#1988](https://github.com/onevcat/Kingfisher/issues/1988) for more information. public func startLoadingBeforeViewAppear(_ flag: Bool = true) -> Self { context.startLoadingBeforeViewAppear = flag return self diff --git a/Sources/SwiftUI/KFImageProtocol.swift b/Sources/SwiftUI/KFImageProtocol.swift index 5a5ad4ba5..cfbb089d6 100644 --- a/Sources/SwiftUI/KFImageProtocol.swift +++ b/Sources/SwiftUI/KFImageProtocol.swift @@ -28,6 +28,12 @@ import SwiftUI import Combine + +/// Represents a view that is compatible with Kingfisher in SwiftUI. +/// +/// As a framework user, you do not need to know the details of this protocol. As the public types, ``KFImage`` and +/// ``KFAnimatedImage`` conform this type and should be used in your app to represent an image view with network and +/// cache support in SwiftUI. @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) public protocol KFImageProtocol: View, KFOptionSetter { associatedtype HoldingView: KFImageHoldingView @@ -45,43 +51,50 @@ extension KFImageProtocol { } } - /// Creates a Kingfisher compatible image view to load image from the given `Source`. + /// Creates an image view compatible with Kingfisher for loading an image from the provided `Source`. + /// /// - Parameters: - /// - source: The image `Source` defining where to load the target image. + /// - source: The `Source` of the image that specifies where to load the target image. public init(source: Source?) { let context = KFImage.Context(source: source) self.init(context: context) } - /// Creates a Kingfisher compatible image view to load image from the given `URL`. + /// Creates an image view compatible with Kingfisher for loading an image from the provided `URL`. + /// /// - Parameters: - /// - source: The image `Source` defining where to load the target image. + /// - url: The `URL` defining the location from which to load the target image. public init(_ url: URL?) { self.init(source: url?.convertToSource()) } - /// Configures current image with a `block` and return another `Image` to use as the final content. + /// Configures the current image with a `block` and returns another `Image` to use as the final content. /// /// This block will be lazily applied when creating the final `Image`. /// - /// If multiple `configure` modifiers are added to the image, they will be evaluated by order. If you want to - /// configure the input image (which is usually an `Image` value) to a non-`Image` value, use `contentConfigure`. + /// If multiple `configure` modifiers are added to the image, they will be evaluated in order. + /// + /// - Parameter block: The block that applies to the loaded image. The block should return an `Image` that is + /// configured. + /// - Returns: A ``KFImage`` or ``KFAnimatedImage`` view that configures the internal `Image` with the provided + /// `block`. /// - /// - Parameter block: The block applies to loaded image. The block should return an `Image` that is configured. - /// - Returns: A `KFImage` view that configures internal `Image` with `block`. + /// > If you want to configure the input image (which is usually an `Image` value) and use a non-`Image` value as + /// > the configured result, use ``KFImageProtocol/contentConfigure(_:)`` instead. public func configure(_ block: @escaping (HoldingView) -> HoldingView) -> Self { context.configurations.append(block) return self } - /// Configures current image with a `block` and return a `View` to use as the final content. + /// Configures the current image with a `block` and returns a `View` to use as the final content. /// /// This block will be lazily applied when creating the final `Image`. /// /// If multiple `contentConfigure` modifiers are added to the image, only the last one will be stored and used. /// /// - Parameter block: The block applies to the loaded image. The block should return a `View` that is configured. - /// - Returns: A `KFImage` view that configures internal `Image` with `block`. + /// - Returns: A ``KFImage`` or ``KFAnimatedImage`` view that configures the internal `Image` with the provided + /// `block`. public func contentConfigure(_ block: @escaping (HoldingView) -> V) -> Self { context.contentConfiguration = { AnyView(block($0)) } return self diff --git a/Sources/Utility/CallbackQueue.swift b/Sources/Utility/CallbackQueue.swift index 822af28e8..b75628536 100644 --- a/Sources/Utility/CallbackQueue.swift +++ b/Sources/Utility/CallbackQueue.swift @@ -28,24 +28,24 @@ import Foundation public typealias ExecutionQueue = CallbackQueue -/// Represents callback queue behaviors when an calling of closure be dispatched. -/// -/// - asyncMain: Dispatch the calling to `DispatchQueue.main` with an `async` behavior. -/// - currentMainOrAsync: Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not -/// `.main`. Otherwise, call the closure immediately in current main queue. -/// - untouch: Do not change the calling queue for closure. -/// - dispatch: Dispatches to a specified `DispatchQueue`. +/// Represents the behavior of the callback queue selection when a closure is dispatched. public enum CallbackQueue { - /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior. + + /// Dispatches the closure to `DispatchQueue.main` with an `async` behavior. case mainAsync - /// Dispatch the calling to `DispatchQueue.main` with an `async` behavior if current queue is not - /// `.main`. Otherwise, call the closure immediately in current main queue. + + /// Dispatches the closure to `DispatchQueue.main` with an `async` behavior if the current queue is not `.main`. + /// Otherwise, it calls the closure immediately on the current main queue. case mainCurrentOrAsync - /// Do not change the calling queue for closure. + + /// Does not change the calling queue for the closure. case untouch - /// Dispatches to a specified `DispatchQueue`. + + /// Dispatches the closure to a specified `DispatchQueue`. case dispatch(DispatchQueue) + /// Executes the `block` in a dispatch queue defined by `self`. + /// - Parameter block: The block needs to be executed. public func execute(_ block: @escaping () -> Void) { switch self { case .mainAsync: diff --git a/Sources/Utility/Delegate.swift b/Sources/Utility/Delegate.swift index 38528947a..3ba0e366d 100644 --- a/Sources/Utility/Delegate.swift +++ b/Sources/Utility/Delegate.swift @@ -25,8 +25,9 @@ // THE SOFTWARE. import Foundation -/// A class that keeps a weakly reference for `self` when implementing `onXXX` behaviors. -/// Instead of remembering to keep `self` as weak in a stored closure: + +/// A class that maintains a weak reference to `self` when implementing `onXXX` behaviors. +/// Instead of manually ensuring that `self` is kept as weak in a stored closure: /// /// ```swift /// // MyClass.swift @@ -46,7 +47,7 @@ import Foundation /// } /// ``` /// -/// You can create a `Delegate` and observe on `self`. Now, there is no retain cycle inside: +/// You can create a `Delegate` and observe it on `self`. This ensures there is no retain cycle: /// /// ```swift /// // MyClass.swift @@ -57,16 +58,15 @@ import Foundation /// /// // ViewController.swift /// var obj: MyClass? -/// +/// /// func doSomething() { /// obj = MyClass() -/// obj!.onDone.delegate(on: self) { (self, _) -/// // `self` here is shadowed and does not keep a strong ref. -/// // So you can release both `MyClass` instance and `ViewController` instance. +/// obj!.onDone.delegate(on: self) { (self, _) in +/// // The `self` here is shadowed and does not retain a strong reference. +/// // Thus, both the `MyClass` instance and the `ViewController` instance can be released. /// self.reportDone() /// } /// } -/// ``` /// public class Delegate { public init() {} diff --git a/Sources/Utility/Result.swift b/Sources/Utility/Result.swift index dcdb08bd6..12c80a4d2 100644 --- a/Sources/Utility/Result.swift +++ b/Sources/Utility/Result.swift @@ -30,7 +30,7 @@ import Foundation // However, they are just wrapper of `ResultUtil` static methods. extension Result where Failure: Error { - /// Evaluates the given transform closures to create a single output value. + /// Evaluates the given transformation closures to create a single output value. /// /// - Parameters: /// - onSuccess: A closure that transforms the success value. diff --git a/Sources/Utility/SizeExtensions.swift b/Sources/Utility/SizeExtensions.swift index 19d05d6b4..e5cd52e1d 100644 --- a/Sources/Utility/SizeExtensions.swift +++ b/Sources/Utility/SizeExtensions.swift @@ -33,8 +33,8 @@ extension KingfisherWrapper where Base == CGSize { /// /// - Parameters: /// - size: The target size to resize to. - /// - contentMode: Content mode of the target size should be when resizing. - /// - Returns: The resized size under the given `ContentMode`. + /// - contentMode: The content mode of the target size when resizing. + /// - Returns: The resized size under the given ``ContentMode``. public func resize(to size: CGSize, for contentMode: ContentMode) -> CGSize { switch contentMode { case .aspectFit: @@ -46,10 +46,10 @@ extension KingfisherWrapper where Base == CGSize { } } - /// Returns a size by resizing the `base` size by making it aspect fitting the given `size`. + /// Returns a size by resizing the `base` size to make it aspect-fit the given `size`. /// - /// - Parameter size: The size in which the `base` should fit in. - /// - Returns: The size fitted in by the input `size`, while keeps `base` aspect. + /// - Parameter size: The size in which the `base` should fit. + /// - Returns: The size that fits the `base` within the input `size`, while keeping the `base` aspect. public func constrained(_ size: CGSize) -> CGSize { let aspectWidth = round(aspectRatio * size.height) let aspectHeight = round(size.width / aspectRatio) @@ -59,10 +59,10 @@ extension KingfisherWrapper where Base == CGSize { CGSize(width: aspectWidth, height: size.height) } - /// Returns a size by resizing the `base` size by making it aspect filling the given `size`. + /// Returns a size by resizing the `base` size to make it aspect-fill the given `size`. /// - /// - Parameter size: The size in which the `base` should fill. - /// - Returns: The size be filled by the input `size`, while keeps `base` aspect. + /// - Parameter size: The size that the `base` should fill. + /// - Returns: The size filled by the input `size`, while keeping the `base` aspect. public func filling(_ size: CGSize) -> CGSize { let aspectWidth = round(aspectRatio * size.height) let aspectHeight = round(size.width / aspectRatio) @@ -72,12 +72,13 @@ extension KingfisherWrapper where Base == CGSize { CGSize(width: aspectWidth, height: size.height) } - /// Returns a `CGRect` for which the `base` size is constrained to an input `size` at a given `anchor` point. + /// Returns a `CGRect` in which the `base` size is constrained to fit within a specified `size`, anchored at a + /// particular `anchor` point. /// /// - Parameters: - /// - size: The size in which the `base` should be constrained to. - /// - anchor: An anchor point in which the size constraint should happen. - /// - Returns: The result `CGRect` for the constraint operation. + /// - size: The size to which the `base` should be constrained. + /// - anchor: The anchor point where the size constraint is applied. + /// - Returns: A `CGRect` that results from the constraint operation. public func constrainedRect(for size: CGSize, anchor: CGPoint) -> CGRect { let unifiedAnchor = CGPoint(x: anchor.x.clamped(to: 0.0...1.0), diff --git a/Sources/Views/AnimatedImageView.swift b/Sources/Views/AnimatedImageView.swift index e6b46bd2e..c2f8d7f0f 100644 --- a/Sources/Views/AnimatedImageView.swift +++ b/Sources/Views/AnimatedImageView.swift @@ -36,19 +36,21 @@ import UIKit import ImageIO -/// Protocol of `AnimatedImageView`. +/// Delegate of the ``AnimatedImageView``. +/// +/// It reports back some events of the animated image view. public protocol AnimatedImageViewDelegate: AnyObject { - /// Called after the animatedImageView has finished each animation loop. + /// Called after the ``AnimatedImageView`` has finished each animation loop. /// /// - Parameters: - /// - imageView: The `AnimatedImageView` that is being animated. - /// - count: The looped count. + /// - imageView: The ``AnimatedImageView`` that is being animated. + /// - count: The loop count. func animatedImageView(_ imageView: AnimatedImageView, didPlayAnimationLoops count: UInt) - /// Called after the `AnimatedImageView` has reached the max repeat count. + /// Called after the ``AnimatedImageView`` has reached the maximum repeat count. /// - /// - Parameter imageView: The `AnimatedImageView` that is being animated. + /// - Parameter imageView: The ``AnimatedImageView`` that is being animated. func animatedImageViewDidFinishAnimating(_ imageView: AnimatedImageView) } @@ -59,15 +61,17 @@ extension AnimatedImageViewDelegate { let KFRunLoopModeCommon = RunLoop.Mode.common -/// Represents a subclass of `UIImageView` for displaying animated image. -/// Different from showing animated image in a normal `UIImageView` (which load all frames at one time), -/// `AnimatedImageView` only tries to load several frames (defined by `framePreloadCount`) to reduce memory usage. -/// It provides a tradeoff between memory usage and CPU time. If you have a memory issue when using a normal image -/// view to load GIF data, you could give this class a try. +/// Represents a subclass of `UIImageView` for displaying animated images. +/// +/// Different from showing an animated image in a normal `UIImageView` (which loads all frames at one time), +/// ``AnimatedImageView`` only tries to load several frames (defined by ``AnimatedImageView/framePreloadCount``) to +/// reduce memory usage. It provides a tradeoff between memory usage and CPU time. If you have a memory issue when +/// using a normal image view to load GIF data, you could give this class a try. /// -/// Kingfisher supports setting GIF animated data to either `UIImageView` and `AnimatedImageView` out of box. So +/// Kingfisher supports setting GIF animated data to either `UIImageView` or ``AnimatedImageView`` out of the box. So /// it would be fairly easy to switch between them. open class AnimatedImageView: UIImageView { + /// Proxy object for preventing a reference cycle between the `CADDisplayLink` and `AnimatedImageView`. class TargetProxy { private weak var target: AnimatedImageView? @@ -81,10 +85,13 @@ open class AnimatedImageView: UIImageView { } } - /// Enumeration that specifies repeat count of GIF + /// An enumeration that specifies the repeat count of a GIF. public enum RepeatCount: Equatable { + /// The animated image should be only played once. case once + /// The animated image should be played by a finite times defined in the associated value. case finite(count: UInt) + /// The animated image should be played infinitely. case infinite public static func ==(lhs: RepeatCount, rhs: RepeatCount) -> Bool { @@ -106,23 +113,30 @@ open class AnimatedImageView: UIImageView { } // MARK: - Public property - /// Whether automatically play the animation when the view become visible. Default is `true`. + /// Whether to automatically play the animation when the view becomes visible. + /// + /// The default is `true`. public var autoPlayAnimatedImage = true - /// The count of the frames should be preloaded before shown. + /// The count of frames that should be preloaded before being shown. public var framePreloadCount = 10 /// Specifies whether the GIF frames should be pre-scaled to the image view's size or not. - /// If the downloaded image is larger than the image view's size, it will help to reduce some memory use. - /// Default is `true`. + /// + /// If the downloaded image is larger than the image view's size, it will help reduce some memory usage. + /// + /// The default is `true`. public var needsPrescaling = true - /// Decode the GIF frames in background thread before using. It will decode frames data and do a off-screen - /// rendering to extract pixel information in background. This can reduce the main thread CPU usage. + /// Decode the GIF frames in a background thread before use. + /// + /// It will decode frames data and perform an off-screen rendering to extract pixel information in the background. + /// This can reduce the main thread's CPU usage. public var backgroundDecode = true - /// The animation timer's run loop mode. Default is `RunLoop.Mode.common`. - /// Set this property to `RunLoop.Mode.default` will make the animation pause during UIScrollView scrolling. + /// The animation timer's run loop mode. The default is `RunLoop.Mode.common`. + /// + /// Setting this property to `RunLoop.Mode.default` will make the animation pause during UIScrollView scrolling. public var runLoopMode = KFRunLoopModeCommon { willSet { guard runLoopMode != newValue else { return } @@ -133,10 +147,11 @@ open class AnimatedImageView: UIImageView { } } - /// The repeat count. The animated image will keep animate until it the loop count reaches this value. - /// Setting this value to another one will reset current animation. + /// The repeat count. The animated image will keep animating until the loop count reaches this value. /// - /// Default is `.infinite`, which means the animation will last forever. + /// Setting this value to another one will reset the current animation. + /// + /// The default is ``RepeatCount/infinite``, which means the animation will last forever. public var repeatCount = RepeatCount.infinite { didSet { if oldValue != repeatCount { @@ -147,10 +162,12 @@ open class AnimatedImageView: UIImageView { } } - /// Delegate of this `AnimatedImageView` object. See `AnimatedImageViewDelegate` protocol for more. + /// The delegate of this `AnimatedImageView` object. + /// + /// See the ``AnimatedImageViewDelegate`` protocol for more information. public weak var delegate: AnimatedImageViewDelegate? - /// The `Animator` instance that holds the frames of a specific image in memory. + /// The ``Animator`` instance that holds the frames of a specific image in memory. public private(set) var animator: Animator? // MARK: - Private property @@ -225,7 +242,6 @@ open class AnimatedImageView: UIImageView { } } - /// Starts the animation. override open func startAnimating() { guard !isAnimating else { return } guard let animator = animator else { return } @@ -234,7 +250,6 @@ open class AnimatedImageView: UIImageView { displayLink.isPaused = false } - /// Stops the animation. override open func stopAnimating() { super.stopAnimating() if isDisplayLinkInitialized { @@ -298,7 +313,7 @@ open class AnimatedImageView: UIImageView { } } - /// Update the current frame with the displayLink duration. + // Update the current frame with the displayLink duration. private func updateFrameIfNeeded() { guard let animator = animator else { return @@ -377,14 +392,14 @@ extension AnimatedImageView { // MARK: - Animator - /// An animator which used to drive the data behind `AnimatedImageView`. + /// An animator which is used to drive the data behind ``AnimatedImageView``. public class Animator { private let size: CGSize private let imageSize: CGSize private let imageScale: CGFloat - /// The maximum count of image frames that needs preload. + /// The maximum count of image frames that need to be preloaded. public let maxFrameCount: Int private let frameSource: ImageFrameSource @@ -412,7 +427,7 @@ extension AnimatedImageView { return frame(at: currentFrameIndex) } - /// The duration of the current active frame duration. + /// The duration of the current active frame. public var currentFrameDuration: TimeInterval { return duration(at: currentFrameIndex) } @@ -525,12 +540,15 @@ extension AnimatedImageView { } /// Gets the image frame of a given index. - /// - Parameter index: The index of desired image. - /// - Returns: The decoded image at the frame. `nil` if the index is out of bound or the image is not yet loaded. + /// - Parameter index: The index of the desired image. + /// - Returns: The decoded image at the frame. `nil` if the index is out of bounds or the image is not yet loaded. public func frame(at index: Int) -> KFCrossPlatformImage? { return animatedFrames[index]?.image } + /// Gets the duration of an image for the given frame index. + /// - Parameter index: The index of the desired image. + /// - Returns: The duration of that frame. public func duration(at index: Int) -> TimeInterval { return animatedFrames[index]?.duration ?? .infinity } diff --git a/Sources/Views/Indicator.swift b/Sources/Views/Indicator.swift index f44721e51..c142e4b1f 100644 --- a/Sources/Views/Indicator.swift +++ b/Sources/Views/Indicator.swift @@ -34,25 +34,25 @@ import UIKit public typealias IndicatorView = UIView #endif -/// Represents the activity indicator type which should be added to -/// an image view when an image is being downloaded. -/// -/// - none: No indicator. -/// - activity: Uses the system activity indicator. -/// - image: Uses an image as indicator. GIF is supported. -/// - custom: Uses a custom indicator. The type of associated value should conform to the `Indicator` protocol. +/// Represents the activity indicator type that should be added to an image view when an image is being downloaded. public enum IndicatorType { + /// No indicator. case none + /// Uses the system activity indicator. case activity - /// Uses an image as indicator. GIF is supported. + + /// Uses an image as an indicator. GIF is supported. case image(imageData: Data) - /// Uses a custom indicator. The type of associated value should conform to the `Indicator` protocol. + + /// Uses a custom indicator. + /// + /// The type of the associated value should conform to the ``Indicator`` protocol. case custom(indicator: Indicator) } -/// An indicator type which can be used to show the download task is in progress. +/// An indicator type which can be used to show that the download task is in progress. public protocol Indicator { /// Called when the indicator should start animating. @@ -61,34 +61,47 @@ public protocol Indicator { /// Called when the indicator should stop animating. func stopAnimatingView() - /// Center offset of the indicator. Kingfisher will use this value to determine the position of - /// indicator in the super view. + /// Center offset of the indicator. + /// + /// Kingfisher will use this value to determine the position of the indicator in the superview. var centerOffset: CGPoint { get } - /// The indicator view which would be added to the super view. + /// The indicator view which would be added to the superview. var view: IndicatorView { get } - /// The size strategy used when adding the indicator to image view. - /// - Parameter imageView: The super view of indicator. + /// The size strategy used when adding the indicator to the image view. + /// - Parameter imageView: The superview of the indicator. + /// - Returns: An ``IndicatorSizeStrategy`` that determines how the indicator should be sized. func sizeStrategy(in imageView: KFCrossPlatformImageView) -> IndicatorSizeStrategy } +/// The idicator size strategy used when sizing the indicator in the image view. public enum IndicatorSizeStrategy { + /// Uses the intrinsic size of the indicator. case intrinsicSize + /// Match the size of the super view of the indicator. case full + /// Uses the associated `CGSize` to set the indicator size. case size(CGSize) } extension Indicator { - /// Default implementation of `centerOffset` of `Indicator`. The default value is `.zero`, means that there is - /// no offset for the indicator view. - public var centerOffset: CGPoint { return .zero } + /// Default implementation of ``Indicator/centerOffset-7jxdw`` of the ``Indicator``. + /// + /// The default value is `.zero`, which means that there is no offset for the indicator view. + public var centerOffset: CGPoint { + .zero + } - /// Default implementation of `centerOffset` of `Indicator`. The default value is `.full`, means that the indicator - /// will pin to the same height and width as the image view. + /// Default implementation of ``Indicator/sizeStrategy(in:)-5x0b4`` of the ``Indicator``. + /// + /// The default value is ``IndicatorSizeStrategy/full``, means that the indicator will pin to the same height and + /// width as the image view. + /// - Parameter imageView: The image view which holds the indicator. + /// - Returns: The desired ``IndicatorSizeStrategy`` public func sizeStrategy(in imageView: KFCrossPlatformImageView) -> IndicatorSizeStrategy { - return .full + .full } }