diff --git a/37-iOS-animation/Animation/Animation.xcodeproj/project.pbxproj b/37-iOS-animation/Animation/Animation.xcodeproj/project.pbxproj index 88af09b..948d1b7 100644 --- a/37-iOS-animation/Animation/Animation.xcodeproj/project.pbxproj +++ b/37-iOS-animation/Animation/Animation.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ B0C0F8152EB8DA5A00A48CCF /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = B0C0F8142EB8DA5A00A48CCF /* SnapKit */; }; B0C0F8192EB8DBBF00A48CCF /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = B0C0F8182EB8DBBF00A48CCF /* Then */; }; + B0DFA4542EF0382D005E61DC /* SkeletonView in Frameworks */ = {isa = PBXBuildFile; productRef = B0DFA4532EF0382D005E61DC /* SkeletonView */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -42,6 +43,7 @@ buildActionMask = 2147483647; files = ( B0C0F8152EB8DA5A00A48CCF /* SnapKit in Frameworks */, + B0DFA4542EF0382D005E61DC /* SkeletonView in Frameworks */, B0C0F8192EB8DBBF00A48CCF /* Then in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -87,6 +89,7 @@ packageProductDependencies = ( B0C0F8142EB8DA5A00A48CCF /* SnapKit */, B0C0F8182EB8DBBF00A48CCF /* Then */, + B0DFA4532EF0382D005E61DC /* SkeletonView */, ); productName = Animation; productReference = B0C0F7F72EB8D82700A48CCF /* Animation.app */; @@ -119,6 +122,7 @@ packageReferences = ( B0C0F8132EB8DA5A00A48CCF /* XCRemoteSwiftPackageReference "SnapKit" */, B0C0F8172EB8DBBF00A48CCF /* XCRemoteSwiftPackageReference "Then" */, + B0DFA4522EF0382D005E61DC /* XCRemoteSwiftPackageReference "SkeletonView" */, ); preferredProjectObjectVersion = 77; productRefGroup = B0C0F7F82EB8D82700A48CCF /* Products */; @@ -362,6 +366,14 @@ minimumVersion = 3.0.0; }; }; + B0DFA4522EF0382D005E61DC /* XCRemoteSwiftPackageReference "SkeletonView" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Juanpe/SkeletonView"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.31.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -375,6 +387,11 @@ package = B0C0F8172EB8DBBF00A48CCF /* XCRemoteSwiftPackageReference "Then" */; productName = Then; }; + B0DFA4532EF0382D005E61DC /* SkeletonView */ = { + isa = XCSwiftPackageProductDependency; + package = B0DFA4522EF0382D005E61DC /* XCRemoteSwiftPackageReference "SkeletonView" */; + productName = SkeletonView; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = B0C0F7EF2EB8D82700A48CCF /* Project object */; diff --git a/37-iOS-animation/Animation/Animation.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/37-iOS-animation/Animation/Animation.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 1566188..bc96552 100644 --- a/37-iOS-animation/Animation/Animation.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/37-iOS-animation/Animation/Animation.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,6 +1,15 @@ { - "originHash" : "0cd11a4ea344b95c25e88fb39b351003de5b41040e76927e86f0f8b8a4edc7a3", + "originHash" : "6f4eb54cfbbcbb68282fe4a50c89d6095a6eb01b9310a9c1eff22896a7e41655", "pins" : [ + { + "identity" : "skeletonview", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Juanpe/SkeletonView", + "state" : { + "revision" : "2f5274827d310e32c09325dd3e0007120940988e", + "version" : "1.31.0" + } + }, { "identity" : "snapkit", "kind" : "remoteSourceControl", diff --git a/37-iOS-animation/Animation/Animation/Application/SceneDelegate.swift b/37-iOS-animation/Animation/Animation/Application/SceneDelegate.swift index f9d1a80..1b2181d 100644 --- a/37-iOS-animation/Animation/Animation/Application/SceneDelegate.swift +++ b/37-iOS-animation/Animation/Animation/Application/SceneDelegate.swift @@ -15,7 +15,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: windowScene) - let vc = UINavigationController(rootViewController: Week02ViewController()) + let vc = UINavigationController(rootViewController: SkeletonViewController()) window.rootViewController = vc self.window = window window.makeKeyAndVisible() diff --git a/37-iOS-animation/Animation/Animation/week04/ProgressBarView.swift b/37-iOS-animation/Animation/Animation/week04/ProgressBarView.swift new file mode 100644 index 0000000..33de2d1 --- /dev/null +++ b/37-iOS-animation/Animation/Animation/week04/ProgressBarView.swift @@ -0,0 +1,56 @@ +// +// ProgressBarView.swift +// Animation +// +// Created by 진소은 on 12/15/25. +// + +import UIKit + +final class ProgressBarView: UIView { + private var circleLayer = CAShapeLayer() + private var progressLayer = CAShapeLayer() + private var startPoint = CGFloat(-Double.pi / 2) + private var endPoint = CGFloat(3 * Double.pi / 2) + + + override func draw(_ rect: CGRect) { + createCircularPath() + } + + private func createCircularPath() { + self.backgroundColor = .white + + let path = UIBezierPath(arcCenter: .init(x: self.frame.width / 2, + y: self.frame.height / 2), + radius: (frame.size.height - 10) / 2, + startAngle: startPoint, + endAngle: endPoint, + clockwise: true) + + circleLayer.path = path.cgPath + circleLayer.fillColor = UIColor.clear.cgColor + circleLayer.lineCap = .round + circleLayer.strokeEnd = 5 + circleLayer.strokeColor = UIColor.black.withAlphaComponent(0.4).cgColor + layer.addSublayer(circleLayer) + + progressLayer.path = path.cgPath + progressLayer.fillColor = UIColor.clear.cgColor + progressLayer.lineCap = .round + progressLayer.lineWidth = 5 + progressLayer.strokeEnd = 0 + progressLayer.strokeColor = UIColor.systemMint.cgColor + layer.addSublayer(progressLayer) + } + + func progressAnimation(duration: TimeInterval, value: Double) { + let circularProgressAnimation = CABasicAnimation(keyPath: "strokeEnd") + circularProgressAnimation.duration = duration + + circularProgressAnimation.toValue = value + circularProgressAnimation.fillMode = .forwards + circularProgressAnimation.isRemovedOnCompletion = false + progressLayer.add(circularProgressAnimation, forKey: "progressAnim") + } +} diff --git a/37-iOS-animation/Animation/Animation/week04/ProgressViewController.swift b/37-iOS-animation/Animation/Animation/week04/ProgressViewController.swift new file mode 100644 index 0000000..03f10c8 --- /dev/null +++ b/37-iOS-animation/Animation/Animation/week04/ProgressViewController.swift @@ -0,0 +1,33 @@ +// +// ProgressViewController.swift +// Animation +// +// Created by 진소은 on 12/15/25. +// + +import UIKit + +import SnapKit +import Then + +final class ProgressViewController: UIViewController { + + private let progressView = ProgressBarView() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + setUI() + + progressView.progressAnimation(duration: 2.0, value: 1.0) + } + + private func setUI() { + view.addSubview(progressView) + + progressView.snp.makeConstraints { + $0.center.equalToSuperview() + $0.size.equalTo(100) + } + } +} diff --git a/37-iOS-animation/Animation/Animation/week04/SkeletonViewController.swift b/37-iOS-animation/Animation/Animation/week04/SkeletonViewController.swift new file mode 100644 index 0000000..d6f31aa --- /dev/null +++ b/37-iOS-animation/Animation/Animation/week04/SkeletonViewController.swift @@ -0,0 +1,61 @@ +// +// SkeletonViewController.swift +// Animation +// +// Created by 진소은 on 12/15/25. +// + +import UIKit + +import SkeletonView +import SnapKit +import Then + +final class SkeletonViewController: UIViewController { + private let label = UILabel() + private let imageView = UIImageView() + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .white + view.isSkeletonable = true + setUI() + setLayout() + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + self.view.showSkeleton() + DispatchQueue.main.asyncAfter(deadline: .now() + 3) { + self.view.hideSkeleton() + } + } + + private func setUI() { + label.do { + $0.text = "듀 !" + $0.textAlignment = .center + $0.isSkeletonable = true + } + imageView.do { + $0.image = UIImage(resource: .imgDamgom1) + $0.isSkeletonable = true + } + } + + private func setLayout() { + view.addSubviews(label, imageView) + + label.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.centerY.equalToSuperview().offset(-100) + $0.horizontalEdges.equalToSuperview().inset(100) + } + + imageView.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalTo(label.snp.bottom).offset(50) + $0.size.equalTo(200) + } + } +}