Skip to content

Commit

Permalink
Merge pull request onevcat#2192 from yeatse/animatedimageview-macos
Browse files Browse the repository at this point in the history
Support `KFAnimatedImage` and `AnimatedImageView` on macOS
  • Loading branch information
onevcat authored Jan 12, 2024
2 parents 3ec0ab0 + 26de05a commit 35ce4f8
Show file tree
Hide file tree
Showing 11 changed files with 561 additions and 31 deletions.
75 changes: 70 additions & 5 deletions Demo/Demo/Kingfisher-macOS-Demo/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
Expand Down Expand Up @@ -708,7 +708,7 @@
</scroller>
</scrollView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="yIr-uo-Quc">
<rect key="frame" x="14" y="447" width="114" height="32"/>
<rect key="frame" x="13" y="448" width="108" height="32"/>
<buttonCell key="cell" type="push" title="Clear Cache" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="ebf-qp-Vwt">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
Expand All @@ -717,8 +717,18 @@
<action selector="clearCachePressedWithSender:" target="XfG-lQ-9wD" id="lfl-RU-nX5"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="eLA-Ce-crP" userLabel="Heavy GIFs">
<rect key="frame" x="259" y="448" width="103" height="32"/>
<buttonCell key="cell" type="push" title="Heavy GIFs" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="YYz-yg-1MB">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<segue destination="19n-lE-hND" kind="show" id="NZO-24-0LI"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Ejh-qu-qmy">
<rect key="frame" x="524" y="447" width="82" height="32"/>
<rect key="frame" x="531" y="448" width="76" height="32"/>
<buttonCell key="cell" type="push" title="Reload" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="DhD-Tg-Bw3">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
Expand All @@ -727,16 +737,31 @@
<action selector="reloadPressedWithSender:" target="XfG-lQ-9wD" id="k24-Wi-NRd"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0Du-le-fYN">
<rect key="frame" x="417" y="448" width="78" height="32"/>
<buttonCell key="cell" type="push" title="SwiftUI" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="wIi-ia-bgi">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<segue destination="ei1-kq-tvV" kind="show" id="Ub0-4z-BqC"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="MlO-xV-qug" secondAttribute="trailing" id="18w-Qc-Jr6"/>
<constraint firstItem="yIr-uo-Quc" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="5" id="35L-th-6RE"/>
<constraint firstItem="Ejh-qu-qmy" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="eLA-Ce-crP" secondAttribute="trailing" constant="12" symbolic="YES" id="7TA-fc-yjU"/>
<constraint firstItem="yIr-uo-Quc" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" constant="20" id="AhA-g2-Cms"/>
<constraint firstItem="Ejh-qu-qmy" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="yIr-uo-Quc" secondAttribute="trailing" constant="12" symbolic="YES" id="BRb-HY-Dny"/>
<constraint firstItem="eLA-Ce-crP" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="yIr-uo-Quc" secondAttribute="trailing" constant="12" symbolic="YES" id="BRb-HY-Dny"/>
<constraint firstItem="MlO-xV-qug" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="30" id="Bzu-9v-r7G"/>
<constraint firstItem="0Du-le-fYN" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="5" id="DMs-p7-JWV"/>
<constraint firstAttribute="bottom" secondItem="MlO-xV-qug" secondAttribute="bottom" id="HY0-vM-k0l"/>
<constraint firstItem="MlO-xV-qug" firstAttribute="leading" secondItem="m2S-Jp-Qdl" secondAttribute="leading" id="Pp3-O7-2Bs"/>
<constraint firstItem="eLA-Ce-crP" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="5" id="cJE-Zj-BEy"/>
<constraint firstAttribute="trailing" secondItem="Ejh-qu-qmy" secondAttribute="trailing" constant="20" id="eoW-Xb-6wq"/>
<constraint firstItem="eLA-Ce-crP" firstAttribute="centerX" secondItem="m2S-Jp-Qdl" secondAttribute="centerX" id="xRm-9b-mgK"/>
<constraint firstItem="Ejh-qu-qmy" firstAttribute="leading" secondItem="0Du-le-fYN" secondAttribute="trailing" constant="50" id="xc2-W0-bu0"/>
<constraint firstItem="Ejh-qu-qmy" firstAttribute="top" secondItem="m2S-Jp-Qdl" secondAttribute="top" constant="5" id="xnX-II-7iN"/>
</constraints>
</view>
Expand All @@ -748,5 +773,45 @@
</objects>
<point key="canvasLocation" x="75" y="831"/>
</scene>
<!--SwiftUI View Controller-->
<scene sceneID="Z7V-ea-dRX">
<objects>
<viewController id="ei1-kq-tvV" customClass="SwiftUIViewController" customModule="Kingfisher_macOS_Demo" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="JMh-S8-QlI">
<rect key="frame" x="0.0" y="0.0" width="620" height="474"/>
<autoresizingMask key="autoresizingMask"/>
</view>
</viewController>
<customObject id="BB8-QC-got" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="75" y="1406"/>
</scene>
<!--Heavy View Controller-->
<scene sceneID="kQM-gs-M6P">
<objects>
<viewController id="19n-lE-hND" customClass="GIFHeavyViewController" customModule="Kingfisher_macOS_Demo" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="4Qc-Su-iB9">
<rect key="frame" x="0.0" y="0.0" width="569" height="480"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView distribution="fillEqually" orientation="vertical" alignment="leading" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="b04-pQ-0pR">
<rect key="frame" x="0.0" y="0.0" width="569" height="480"/>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="b04-pQ-0pR" secondAttribute="bottom" id="WP7-Ep-JhF"/>
<constraint firstItem="b04-pQ-0pR" firstAttribute="leading" secondItem="4Qc-Su-iB9" secondAttribute="leading" id="t0Z-wK-nRD"/>
<constraint firstAttribute="trailing" secondItem="b04-pQ-0pR" secondAttribute="trailing" id="yeI-o0-y8O"/>
<constraint firstItem="b04-pQ-0pR" firstAttribute="top" secondItem="4Qc-Su-iB9" secondAttribute="top" id="yhl-hr-4Mw"/>
</constraints>
</view>
<connections>
<outlet property="stackView" destination="b04-pQ-0pR" id="nsI-xM-cZe"/>
</connections>
</viewController>
<customObject id="Hmf-j6-h1n" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="766.5" y="831"/>
</scene>
</scenes>
</document>
58 changes: 58 additions & 0 deletions Demo/Demo/Kingfisher-macOS-Demo/GIFHeavyViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// GIFHeavyViewController.swift
// Kingfisher
//
// Created by yeatse on 2024/1/7.
//
// Copyright (c) 2024 Wei Wang <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import Cocoa
import Kingfisher

class GIFHeavyViewController: NSViewController {
@IBOutlet weak var stackView: NSStackView!

let imageViews = [
AnimatedImageView(),
AnimatedImageView(),
AnimatedImageView(),
AnimatedImageView(),
]

override func viewDidLoad() {
super.viewDidLoad()

let url = URL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/GIF/GifHeavy.gif")

for imageView in imageViews {
stackView.addArrangedSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.widthAnchor.constraint(equalTo: stackView.widthAnchor).isActive = true
imageView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
imageView.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
imageView.imageScaling = .scaleProportionallyDown
}
stackView.layoutSubtreeIfNeeded()
for imageView in imageViews {
imageView.kf.setImage(with: url)
}
}
}
79 changes: 79 additions & 0 deletions Demo/Demo/Kingfisher-macOS-Demo/SwiftUIViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// SwiftUIViewController.swift
// Kingfisher
//
// Created by yeatse on 2024/1/8.
//
// Copyright (c) 2024 Wei Wang <[email protected]>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

import SwiftUI
import Kingfisher

@available(macOS 11, *)
class SwiftUIViewController: NSHostingController<MainView> {
required init?(coder: NSCoder) {
super.init(coder: coder, rootView: MainView())
}
}

@available(macOS 11, *)
struct MainView: View {
@State private var index = 1

static let gifImageURLs: [URL] = {
let prefix = "https://raw.githubusercontent.com/onevcat/Kingfisher-TestImages/master/DemoAppImage/GIF"
return (1...3).map { URL(string: "\(prefix)/\($0).gif")! }
}()

var url: URL {
MainView.gifImageURLs[index - 1]
}

var body: some View {
VStack {
KFAnimatedImage(url)
.configure { view in
view.framePreloadCount = 3
}
.cacheOriginalImage()
.onSuccess { r in
print("suc: \(r)")
}
.onFailure { e in
print("err: \(e)")
}
.placeholder { p in
ProgressView(p)
}
.fade(duration: 1)
.forceTransition()
.aspectRatio(contentMode: .fill)
.frame(width: 300, height: 300)
.cornerRadius(20)
.shadow(radius: 5)
.frame(width: 320, height: 320)

Button(action: {
self.index = (self.index % 3) + 1
}) { Text("Next Image") }
}
}
}
8 changes: 8 additions & 0 deletions Demo/Kingfisher-Demo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
277EAE9D2045B4D500547CD3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 277EAE962045B4D500547CD3 /* Assets.xcassets */; };
277EAEA12045B52800547CD3 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277EAE9E2045B52800547CD3 /* InterfaceController.swift */; };
277EAEA32045B52800547CD3 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 277EAEA02045B52800547CD3 /* ExtensionDelegate.swift */; };
3887E1602B4AD7200062C53C /* GIFHeavyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887E15F2B4AD7200062C53C /* GIFHeavyViewController.swift */; };
3887E17B2B4BC04B0062C53C /* SwiftUIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3887E17A2B4BC04B0062C53C /* SwiftUIViewController.swift */; };
4B120CA726B91BB70060B092 /* TransitionViewDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B120CA626B91BB70060B092 /* TransitionViewDemo.swift */; };
4B1C7A3D21A256E300CE9D31 /* InfinityCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B1C7A3C21A256E300CE9D31 /* InfinityCollectionViewController.swift */; };
4B4307A51D87E6A700ED2DA9 /* loader.gif in Resources */ = {isa = PBXBuildFile; fileRef = 4B7742461D87E42E0077024E /* loader.gif */; };
Expand Down Expand Up @@ -161,6 +163,8 @@
277EAE9E2045B52800547CD3 /* InterfaceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = "<group>"; };
277EAE9F2045B52800547CD3 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
277EAEA02045B52800547CD3 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = "<group>"; };
3887E15F2B4AD7200062C53C /* GIFHeavyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFHeavyViewController.swift; sourceTree = "<group>"; };
3887E17A2B4BC04B0062C53C /* SwiftUIViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIViewController.swift; sourceTree = "<group>"; };
4B120CA626B91BB70060B092 /* TransitionViewDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransitionViewDemo.swift; sourceTree = "<group>"; };
4B1C7A3C21A256E300CE9D31 /* InfinityCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfinityCollectionViewController.swift; sourceTree = "<group>"; };
4B2944551C3D03880088C3E7 /* Kingfisher-macOS-Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Kingfisher-macOS-Demo.app"; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -318,6 +322,8 @@
4BCCF33A1D5B02F8003387C2 /* Cell.xib */,
4BCCF33B1D5B02F8003387C2 /* Info.plist */,
4BCCF33C1D5B02F8003387C2 /* ViewController.swift */,
3887E15F2B4AD7200062C53C /* GIFHeavyViewController.swift */,
3887E17A2B4BC04B0062C53C /* SwiftUIViewController.swift */,
);
name = "Kingfisher-macOS-Demo";
path = "Demo/Kingfisher-macOS-Demo";
Expand Down Expand Up @@ -655,7 +661,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3887E17B2B4BC04B0062C53C /* SwiftUIViewController.swift in Sources */,
4BCCF3421D5B02F8003387C2 /* ViewController.swift in Sources */,
3887E1602B4AD7200062C53C /* GIFHeavyViewController.swift in Sources */,
4BCCF33D1D5B02F8003387C2 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Loading

0 comments on commit 35ce4f8

Please sign in to comment.