Skip to content

Instantly share code, notes, and snippets.

@CodeSlicing
Last active February 17, 2020 21:39
Show Gist options
  • Save CodeSlicing/e61c2d448d6c5e1e5f849e29e26e6f47 to your computer and use it in GitHub Desktop.
Save CodeSlicing/e61c2d448d6c5e1e5f849e29e26e6f47 to your computer and use it in GitHub Desktop.
Demonstrates animating a layout guide to create a rain cloud icon
//
// RainIconDemo.swift
//
// 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 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.
//
// Created by Adam Fordyce on 17/02/2020.
// Copyright © 2020 Adam Fordyce. All rights reserved.
//
import SwiftUI
import PureSwiftUI
struct RainIconAnimationDemo: View {
var body: some View {
VStack(spacing: 20) {
RainIconDemoView(showShapeConstruction: false)
RainIconDemoView(showShapeConstruction: true)
}
}
}
private struct RainIconDemoView: View {
@State private var animating = false
let showShapeConstruction: Bool
var body: some View {
ZStack {
AnimatedRainShape(factor: animating ? 1 : 0)
.stroke(style: .init(lineWidth: 5, lineCap: .round, lineJoin: .round))
.clipped()
.xOffset(14)
.frame(150)
ZStack {
CloudShape(showControlPoints: showShapeConstruction)
.fillColor(showShapeConstruction ? .clear : Color(white: 0.6))
CloudShape(showControlPoints: showShapeConstruction)
.stroke(style: .init(lineWidth: 5, lineCap: .round, lineJoin: .round))
}
.layoutGuide(cloudLayoutConfig)
.frame(150)
.yOffset(-70)
}
.yOffset(50)
.frame(250)
.showLayoutGuides(showShapeConstruction)
.clipRoundedRectangle(20, fill: Color.white)
.shadow(10)
.onAppear {
withAnimation(Animation.linear(duration: 0.4).repeatForever(autoreverses: false)) {
self.animating.toggle()
}
}
}
}
fileprivate let rainLayoutConfig = LayoutGuideConfig.grid(columns: 10, rows: 10)
fileprivate let cloudLayoutConfig = LayoutGuideConfig.grid(columns: 20, rows: 20)
private struct CloudShape: Shape {
let showControlPoints: Bool
func path(in rect: CGRect) -> Path {
var path = Path()
var grid = cloudLayoutConfig.layout(in: rect)
path.move(grid[3, 10])
path.curve(grid[15, 8], cp1: grid[4, 3], cp2: grid[12,2], showControlPoints: showControlPoints)
path.curve(grid[16, 17], cp1: grid[22, 8], cp2: grid[21, 17], showControlPoints: showControlPoints)
path.line(grid[4, 17])
path.curve(grid[3, 10], cp1: grid[-1, 17], cp2: grid[-1, 11], showControlPoints: showControlPoints)
return path
}
}
private struct AnimatedRainShape: Shape {
var factor: CGFloat
var animatableData: CGFloat {
get {
factor
}
set {
factor = newValue
}
}
func path(in rect: CGRect) -> Path {
var path = Path()
var grid = rainLayoutConfig.layout(in: rect)
.offset(.point(-5, -rect.halfHeight))
.offset(.y(rect.halfHeight), factor: factor)
.rotated(-20.degrees)
for index in 0...2 {
let offset = 5 * index
path.line(from: grid[2, 3 + offset], to: grid[2, 5 + offset])
path.line(from: grid[4, 5 + offset], to: grid[4, 8 + offset])
path.line(from: grid[6, 4 + offset], to: grid[6, 6 + offset])
path.line(from: grid[8, 2 + offset], to: grid[8, 5 + offset])
}
return path
}
}
struct RainPathAnimationDemo_Previews: PreviewProvider {
struct RainPathAnimationDemo_Harness: View {
var body: some View {
RainIconAnimationDemo()
}
}
static var previews: some View {
RainPathAnimationDemo_Harness()
.showLayoutGuides(true)
.padding(100)
.previewSizeThatFits()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment