Skip to content

Challenge -2: AirDrop animation #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "BBF49788-BB1A-498A-A1E5-9A964B5FE8C6"
type = "1"
version = "2.0">
</Bucket>
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>AirDropAnimation_Example.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// AirDropAnimation_ExampleApp.swift
// AirDropAnimation_Example
//
// Created by Shanmukh Phulari on 05/08/22.
//

import SwiftUI

@main
struct AirDropAnimation_ExampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
179 changes: 179 additions & 0 deletions AirDropAnimation_Example/AirDropAnimation_Example/AirDropView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
//
// AirDropView.swift
// AirDropAnimation_Example
//
// Created by Shanmukh Phulari on 05/08/22.
//

import SwiftUI

//Progress status Enum
enum ProgressStatus {
case none, notconnected, connecting, connected, started, completed
}

struct AirDropView: View {
let size : CGSize
let personName : String
let personImage : UIImage
let deviceName : String
let progress : Double
let progressStatus : ProgressStatus;

//State variable
@State var blinking = false;
@State private var progressStatusText : String = getProgressStatusText(progress: .notconnected)
@State private var progressStatusTextColor : Color = defaultProgressStatusTextColor
@State private var backgroundProgressStrokeColor = progressGrayColor
@State private var foregroundProgressStrokeColor = progressBlueColor
@State private var imageBorderStrokeColor = progressBlackColor

//Timer to do animation
@State private var blinkTimer = Timer.publish(every: .infinity, tolerance: 0, on: .main, in: RunLoop.Mode.common, options: nil).autoconnect()

var body: some View {
VStack {
ZStack{
//default progress circle
Circle()
.stroke(backgroundProgressStrokeColor, lineWidth: size.width*stokeWidthRatio)
.background(Circle().fill(backgroundProgressColor))

//progress circle
Circle()
.trim(from: 0.0, to:progress)
.stroke(foregroundProgressStrokeColor, lineWidth: size.width*stokeWidthRatio)
.rotationEffect(.degrees(-90))
.animation(.easeOut(duration:progress), value: progress)

//person image border
Circle()
.inset(by: (size.width*stokeWidthRatio)/3)
.stroke(imageBorderStrokeColor, lineWidth: (size.width*stokeWidthRatio)/3)

//persone image
Image(uiImage: personImage)
.resizable()
.scaledToFit()

}.padding()

//Device name
Text(deviceName)
.font(.headline)
.foregroundColor(defaultProgressStatusTextColor)

//Progress status
Text(progressStatusText)
.font(.headline)
.foregroundColor(progressStatusTextColor)
.opacity(blinking ? 0 : 1)
.animation(blinking ? .easeIn(duration: blinkTimerValue ) : .easeOut(duration: blinkTimerValue), value: blinking)
.onReceive(blinkTimer, perform: { _ in
blinking = !blinking;
})
}
.onChange(of: progressStatus, perform: { newValue in

switch newValue {
case .notconnected:
onNotConnected(status: newValue)
case .connecting:
onConnecting(status: newValue)
case .connected:
break
case .started:
onStarted(status: newValue)
case .completed:
//Adding delay to complete animation
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 1.0) {
onFinish(status: newValue)
}
case .none:
break
}
})
.frame(width: size.width, height: size.height, alignment: .center)
}

private func onNotConnected(status:ProgressStatus) {

//iphone connected
progressStatusText = AirDropView.getProgressStatusText(progress: status) + personName;

progressStatusTextColor = defaultProgressStatusTextColor

//reset stroke color
backgroundProgressStrokeColor = progressGrayColor
foregroundProgressStrokeColor = progressBlueColor
imageBorderStrokeColor = progressBlackColor

//reset blinking
blinkTimer.upstream.connect().cancel()
blinkTimer = Timer.publish(every: .infinity, tolerance: 0, on: .main, in: RunLoop.Mode.common, options: nil).autoconnect()

}

private func onConnecting(status:ProgressStatus) {

progressStatusText = AirDropView.getProgressStatusText(progress: status);

progressStatusTextColor = defaultProgressStatusTextColor

//reset blinking
blinkTimer.upstream.connect().cancel()
blinkTimer = Timer.publish(every: blinkTimerValue, tolerance: 0, on: .main, in: RunLoop.Mode.common, options: nil).autoconnect()
}

private func onStarted(status:ProgressStatus) {

//Stop blinking
blinking = false;
blinkTimer.upstream.connect().cancel()

//iphone started sending
progressStatusText = AirDropView.getProgressStatusText(progress: status);

progressStatusTextColor = defaultProgressStatusTextColor
}

private func onFinish(status:ProgressStatus) {

//iphone completes sending
progressStatusText = AirDropView.getProgressStatusText(progress: status);

progressStatusTextColor = completedProgressStatusTextColor

withAnimation(Animation.easeOut(duration: 0.3).delay(1)) {
backgroundProgressStrokeColor = Color.clear
foregroundProgressStrokeColor = Color.clear
imageBorderStrokeColor = Color.clear
}
}

static func getProgressStatusText(progress:ProgressStatus) -> String {
var returnValue = "";
switch progress {
case .notconnected:
returnValue = "from ";
case .connecting:
returnValue = "Waiting...";
case .connected:
returnValue = "Connected";
case .started:
returnValue = "Sending..."
case .completed:
returnValue = "Sent"
case .none:
returnValue = ""
}
return returnValue;
}

}

struct AirDropView_Previews: PreviewProvider {
static var previews: some View {
AirDropView(size: CGSize.zero, personName: "Amos", personImage: UIImage(named: "person")!, deviceName: "iPhone", progress: 0, progressStatus: .notconnected)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
22 changes: 22 additions & 0 deletions AirDropAnimation_Example/AirDropAnimation_Example/Constant.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Constant.swift
// AirDropAnimation_Example
//
// Created by Shanmukh Phulari on 09/08/22.
//

import Foundation
import SwiftUI


let stokeWidthRatio = 0.05;
let blinkTimerValue : Double = 0.6
let waitingTimerValue : Double = 4.0
let progressTimerValue : Double = 0.1

let backgroundProgressColor = Color.init(red: 153.0/255.0, green: 154.0/255.0, blue: 154.0/255.0)
let defaultProgressStatusTextColor : Color = Color.init(red: 222.0/255.0, green: 222.0/255.0, blue: 222.0/255.0)
let completedProgressStatusTextColor : Color = Color.init(red: 53.0/255.0, green: 134.0/255.0, blue: 247.0/255.0)
let progressGrayColor : Color = Color.init(red: 104.0/255.0, green: 104.0/255.0, blue: 108.0/255.0)
let progressBlueColor : Color = Color.init(red: 53.0/255.0, green: 134.0/255.0, blue: 247.0/255.0)
let progressBlackColor : Color = Color.init(red: 32.0/255.0, green: 32.0/255.0, blue: 32.0/255.0)
Loading