Skip to content

🌲 Pure SwiftUI christmas tree with yearly updates. Enjoy πŸŽ„

Notifications You must be signed in to change notification settings

barnejs/SwiftUIChristmasTree

Β 
Β 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

26 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸŽ„ The SwiftUI Christmas Tree 🌲

Pure SwiftUI Christmas Tree and Fireworks Animations with yearly updates

Explore the Xcode project folder and check out the various SwiftUI animation files. Enjoy.

SwiftUI Christmas Tree

2024 Christmas Tree

import SwiftUI

struct SwiftUIXmasTree24: View {
    @State private var isSpinning = false

    // MARK: - Constants
    private enum Constants {
        static let animationDuration = 1.5
        static let rotation3D = 60.0
        static let ornamentCount = 4
        static let spinAngle = 310.0
        static let staticAngle = 50.0
    }

    // MARK: - White Shades
    private let whiteShades = (1...10).map { Color.white.opacity(1.1 - Double($0) * 0.1) }

    // MARK: - Circle Configurations
    private struct CircleConfig {
        let size: CGFloat
        let strokeWidth: CGFloat
        let yOffset: CGFloat
        let ornament: String
        let ornamentSize: CGFloat
        let ornamentOffset: CGFloat
    }

    private let circleConfigs: [CircleConfig] = [
        CircleConfig(size: 20, strokeWidth: 1, yOffset: -160, ornament: "✨", ornamentSize: 4, ornamentOffset: -10),
        CircleConfig(size: 50, strokeWidth: 2, yOffset: -120, ornament: "🌟", ornamentSize: 6, ornamentOffset: -25),
        CircleConfig(size: 80, strokeWidth: 3, yOffset: -80, ornament: "β˜…", ornamentSize: 8, ornamentOffset: -40),
        CircleConfig(size: 110, strokeWidth: 3, yOffset: -40, ornament: "⭐️", ornamentSize: 8, ornamentOffset: -55),
        CircleConfig(size: 140, strokeWidth: 3, yOffset: 0, ornament: "✨", ornamentSize: 10, ornamentOffset: -70),
        CircleConfig(size: 170, strokeWidth: 3, yOffset: 40, ornament: "✰", ornamentSize: 8, ornamentOffset: -85),
        CircleConfig(size: 200, strokeWidth: 5, yOffset: 80, ornament: "✧", ornamentSize: 10, ornamentOffset: -100),
        CircleConfig(size: 230, strokeWidth: 4, yOffset: 120, ornament: "β™’", ornamentSize: 10, ornamentOffset: -115),
        CircleConfig(size: 260, strokeWidth: 5, yOffset: 160, ornament: "⭐︎", ornamentSize: 12, ornamentOffset: -130),
        CircleConfig(size: 290, strokeWidth: 5, yOffset: 200, ornament: "✦", ornamentSize: 12, ornamentOffset: -145)
    ]

    // MARK: - Helper Views
    private func CircleLayer(config: CircleConfig, index: Int) -> some View {
        ZStack {
            Circle()
                .stroke(lineWidth: config.strokeWidth)
                .frame(width: config.size, height: config.size)
                .foregroundStyle(whiteShades[index].gradient)

            ForEach(0..<Constants.ornamentCount, id: \.self) { i in
                Text(config.ornament)
                    .hueRotation(.degrees(isSpinning ? Double(i) * Constants.spinAngle : Double(i) * Constants.staticAngle))
                    .offset(y: config.ornamentOffset)
                    .rotationEffect(.degrees(Double(i) * -90))
                    .rotationEffect(.degrees(isSpinning ? 0 : -180))
                    .frame(width: config.ornamentSize, height: config.ornamentSize)
                    .animation(
                        .linear(duration: Constants.animationDuration)
                        .repeatForever(autoreverses: false)
                        .delay(Double(index) * 0.1),
                        value: isSpinning
                    )
            }
        }
        .rotation3DEffect(.degrees(Constants.rotation3D), axis: (x: 1, y: 0, z: 0))
        .offset(y: config.yOffset)
    }

    var body: some View {
        VStack(spacing: 10) {
            Image(systemName: "gift.circle")
                .font(.largeTitle)
                .phaseAnimator([false , true]) { sparkleSymbol, animate in
                    sparkleSymbol
                        .symbolEffect(.wiggle.byLayer, options: .repeat(3), value: animate)
                        .symbolEffect(.bounce.byLayer, options: .repeat(3), value: animate)
                        .symbolEffect(.pulse.byLayer, options: .repeat(3), value: animate)
                        .symbolEffect(.breathe.byLayer, options: .repeat(3), value: animate)
                }

            ZStack {
                ForEach(Array(circleConfigs.enumerated()), id: \.offset) { index, config in
                    CircleLayer(config: config, index: index)
                }
            }
            .onAppear { isSpinning.toggle() }
        }
    }
}

#Preview {
    SwiftUIXmasTree24()
        .preferredColorScheme(.dark)
}

2023 Christmas Tree

//  MARK: 2023 Christmas Tree
//  SwiftUIChristmasTree.swift
//  SwiftUIChristmasTree
//

import SwiftUI

struct SwiftUIXmasTree: View {

    @State private var isSpinning = false
    let coral = Color(#colorLiteral(red: 1, green: 0.4941176471, blue: 0.4745098039, alpha: 1))
    let peach = Color(#colorLiteral(red: 1, green: 0.831372549, blue: 0.4745098039, alpha: 1))
    let lightLimeGreen = Color(#colorLiteral(red: 0.831372549, green: 0.9843137255, blue: 0.4745098039, alpha: 1))
    let springGreen = Color(#colorLiteral(red: 0.2862745098, green: 0.9803921569, blue: 0.4745098039, alpha: 1))
    let paleAqua = Color(#colorLiteral(red: 0.2862745098, green: 0.9882352941, blue: 0.8392156863, alpha: 1))
    let skyBlue = Color(#colorLiteral(red: 0.2901960784, green: 0.8392156863, blue: 1, alpha: 1))
    let softLavender = Color(#colorLiteral(red: 0.4784313725, green: 0.5058823529, blue: 1, alpha: 1))
    let electricPurple = Color(#colorLiteral(red: 0.8470588235, green: 0.5137254902, blue: 1, alpha: 1))
    let olive = Color(#colorLiteral(red: 0.5764705882, green: 0.3529411765, blue: 0, alpha: 1))
    let forestGreen = Color(#colorLiteral(red: 0, green: 0.5607843137, blue: 0, alpha: 1))

    var body: some View {
        VStack {
            //Image(systemName: "wand.and.stars.inverse")
            Text("🎁")
                .font(.system(size: 64))
                .hueRotation(.degrees(isSpinning ? 0 : 150))
                .animation(.easeInOut(duration: 1).repeatForever(autoreverses: true).delay(0.5), value: isSpinning)

            PresentView()

            ZStack {
                ZStack {
                    Circle() // MARK: One. No delay
                        .stroke(lineWidth: 1)
                        .frame(width: 20, height: 20)
                        .foregroundStyle(coral.gradient)

                    ForEach(0 ..< 4) {
                        //Circle()
                        Text("✨")
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -10)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 4, height: 4)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: -160)

                ZStack {
                    Circle() // MARK: Two. 0.1 delay
                        .stroke(lineWidth: 2)
                        .frame(width: 50, height: 50)
                        .foregroundStyle(peach.gradient)

                    ForEach(0 ..< 4) {
                        //Circle()
                        Text("🌟")
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -25)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 6, height: 6)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.1), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: -120)

                ZStack {
                    Circle() // Three. 0.2 delay
                        .stroke(lineWidth: 3)
                        .frame(width: 80, height: 80)
                        .foregroundStyle(lightLimeGreen.gradient)

                    ForEach(0 ..< 4) {
                        //Circle()
                        Text("πŸ’«")
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -40)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 8, height: 8)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.2), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: -80)

                ZStack {
                    Circle() // MARK: Four. 0.3 delay
                        .stroke(lineWidth: 3)
                        .frame(width: 110, height: 110)
                        .foregroundStyle(springGreen.gradient)

                    ForEach(0 ..< 4) {
                        Circle()
                            .foregroundColor(.red)
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -55)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 8, height: 8)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.3), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: -40)

                ZStack {
                    Circle() // MARK: Five. 0.4 delay
                        .stroke(lineWidth: 3)
                        .frame(width: 140, height: 140)
                        .foregroundStyle(paleAqua.gradient)

                    ForEach(0 ..< 4) {
                        Circle()
                            .foregroundStyle(.red.gradient)
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -70)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 10, height: 10)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.4), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: 0)

                ZStack {
                    Circle() // MARK: Six. 0.5 delay
                        .stroke(lineWidth: 3)
                        .frame(width: 170, height: 170)
                        .foregroundStyle(skyBlue.gradient)

                    ForEach(0 ..< 4) {
                        Circle()
                            .foregroundStyle(.red.gradient)
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -85)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 8, height: 8)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.5), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: 40)

                ZStack {
                    Circle() // MARK: Seven. 0.6 delay
                        .stroke(lineWidth: 5)
                        .frame(width: 200, height: 200)
                        .foregroundStyle(softLavender.gradient)

                    ForEach(0 ..< 4) {
                        Image(systemName: "star")
                            .foregroundStyle(.red.gradient)
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -100)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.6), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: 80)

                ZStack {
                    Circle() // MARK: Eight. 0.7 delay
                        .stroke(lineWidth: 4)
                        .frame(width: 230, height: 230)
                        .foregroundStyle(electricPurple.gradient)

                    ForEach(0 ..< 4) {
                        Circle()
                            .foregroundStyle(.red.gradient)
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -115)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 10, height: 10)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.7), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: 120)

                ZStack {
                    Circle() // MARK: Nine. 0.8 delay
                        .stroke(lineWidth: 5)
                        .frame(width: 260, height: 260)
                        .foregroundStyle(olive.gradient)

                    ForEach(0 ..< 4) {
                        //Circle()
                        Text("🧧")
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -130)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .frame(width: 12, height: 12)
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.8), value: isSpinning)
                    }
                }
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: 160)


                ZStack {
                    Circle() // MARK: Ten. 0.9 delay
                        .stroke(lineWidth: 5)
                        .foregroundStyle(forestGreen.gradient)

                    ForEach(0 ..< 4) {
                        Text("πŸ’")
                            .rotationEffect(.degrees(-45))
                            .hueRotation(.degrees(isSpinning ? Double($0) * 310 : Double($0) * 50))
                            .offset(y: -145)
                            .rotationEffect(.degrees(Double($0) * -90))
                            .rotationEffect(.degrees(isSpinning ? 0 : -180))
                            .animation(.linear(duration: 1.5).repeatForever(autoreverses: false).delay(0.9), value: isSpinning)
                    }
                }
                .frame(width: 290, height: 290)
                .rotation3DEffect(.degrees(60), axis: (x: 1, y: 0, z: 0))
                .offset(y: 200)
            }
            .onAppear() {
                isSpinning.toggle()
            }
        }
    }
}

#Preview {
    SwiftUIXmasTree()
        .preferredColorScheme(.dark)
}

SwiftUI Christmas tree and fireworks: Coming on 31/12/2024. Stay tuned.

About

🌲 Pure SwiftUI christmas tree with yearly updates. Enjoy πŸŽ„

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Swift 100.0%