Thursday, February 2, 2023
Learning Code
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#
No Result
View All Result
Learning Code
No Result
View All Result
Home Swift

Special Effects with SwiftUI – Hacking with Swift

learningcode_x1mckf by learningcode_x1mckf
September 9, 2022
in Swift
0
Special Effects with SwiftUI – Hacking with Swift
74
SHARES
1.2k
VIEWS
Share on FacebookShare on Twitter



You might also like

The abstract Vapor service factory design pattern

SwiftNIO tutorial – The echo server

Introducing – Vapor cheatsheet – The.Swift.Dev.

Everybody is aware of SwiftUI does a fantastic job of making customary system layouts with lists, buttons, navigation views, and extra, and whereas it’s actually helpful I doubt most individuals would name it enjoyable.

So, on this article I’m going to point out you easy methods to use SwiftUI to construct one thing enjoyable, lovely, and in contrast to something you’ve seen earlier than. You’ll want not less than Xcode 13 and iOS 15 or later, however you additionally must obtain a single picture from my website from right here: https://hws.dev/spark.zip.

Sponsor Hacking with Swift and reach the world’s largest Swift community!

Getting began

On the core of our little experiment is a particle system, which is often utilized in video games to create results like hearth, smoke, rain, and extra. We’re going to begin easy and work our approach up – it’s fairly wonderful how briskly we will transfer with SwiftUI.

Step one is straightforward: create a brand new iOS mission utilizing the App template, ensuring to decide on SwiftUI to your interface. It is best to have already got downloaded the spark.zip file from my website, which comprises a single picture. I’d such as you to tug that into your mission’s asset catalog, so now we have a picture to make use of for all our particles.

Subsequent, I’d like to consider what it means to retailer one particle in a particle system – what does one rain drop must retailer, or one snowflake? There are all kinds of values we might retailer, however we’d like solely three: the X and Y coordinates for the particle, plus the date it was created.

Along with that, I’m additionally going so as to add a conformance to the Hashable protocol, so we will add our particles to a set and take away them simply.

Create a brand new Swift file referred to as Particle.swift, and provides it this code:

struct Particle: Hashable 
    let x: Double
    let y: Double
    let creationDate = Date.now.timeIntervalSinceReferenceDate

So, that shops a single particle in our particle system – that’s one drop of rain, one spark from a fireplace, one piece of fairy mud, or no matter type of particles you’re working with.

One degree up from that’s the particle system itself, which wants three items of knowledge:

  1. The picture it ought to render.
  2. A sequence containing all of the particles which can be lively proper now.
  3. The place it must be positioned in our UI.

The primary two of these are easy, however the third must be handled rigorously: we don’t wish to need to hard-code values such because the width or top of our system, as a result of it wouldn’t scale effectively throughout varied system sizes. So, as an alternative we’re going to make use of the identical system SwiftUI makes use of for issues like anchors and gradient positions: the UnitPoint kind, which shops its values from 0 by means of 1 for each X and Y coordinates.

Create one other new Swift file referred to as ParticleSystem.swift, and provides it this code:

class ParticleSystem 
    let picture = Picture("spark")
    var particles = Set<Particle>()
    var middle = UnitPoint.middle

To deliver that to life, we’d like a approach to replace our particles – to take away any which can be outdated, and create new ones frequently. For now this can simply be so simple as creating a brand new particle every time a perform is known as, however we’ll add to it afterward.

Add this to the ParticleSystem class now:

func replace(date: TimeInterval) 
    let newParticle = Particle(x: middle.x, y: middle.y)
    particles.insert(newParticle)

The ultimate a part of our preliminary step is to jot down some SwiftUI code to render our particles. This may be finished actually effectively due to SwiftUI’s TimelineView and Canvas: the previous lets us render content material on a frequent interval, and the latter lets us render textual content, photographs, shapes, and extra.

This mix is ideal for our functions as a result of it lets us render the particle picture time and again – as soon as for each particle – and so is extraordinarily environment friendly.

So, we will begin by including a property to ContentView that can retailer our particle system:

@State non-public var particleSystem = ParticleSystem()

Sure, that makes use of @State although ParticleSystem is a category – it successfully acts as a cache for the reference kind, with out triggering modifications when any a part of the category modifications.

Now we will fill within the fundamentals of our view’s physique: we’re going to create a TimelineView on an animation schedule, which can give us clean motion, then place inside {that a} Canvas so we will do our customized particle drawing. The initializers for each of those sorts move in values for us to make use of: how briskly the timeline updates and what its present time is, and a drawing context and measurement respectively.

Begin by changing your present ContentView physique with this:

TimelineView(.animation)  timeline in
    Canvas  context, measurement in
        // drawing code right here
    

.ignoresSafeArea()
.background(.black)

I’ve made which have a black, edge to edge background so our particles stand out good and clearly.

Contained in the drawing code we have to do two issues:

  1. Name our particle system’s replace() technique with the present date as a TimeInterval. This isn’t used proper now, however is vital shortly.
  2. Draw every particle. Keep in mind, particle positions are saved as X/Y values between 0 and 1, the place 0 is the left or high edge, and 1 is the best or backside edge. So, we will a number of every particle’s place by the dimensions of our canvas to get the precise drawing place.

Change the // drawing code right here remark with this:

let timelineDate = timeline.date.timeIntervalSinceReferenceDate
particleSystem.replace(date: timelineDate)

for particle in particleSystem.particles 
    let xPos = particle.x * measurement.width
    let yPos = particle.y * measurement.top
    context.draw(particleSystem.picture, at: CGPoint(x: xPos, y: yPos))

That’s sufficient to make our program run, so give it a strive! If all the pieces has gone to plan, it’s best to see a white circle within the middle and never a lot else. That’s hardly spectacular by any customary, notably given I mentioned we might transfer quick with SwiftUI.

Going up a gear

Most of our work thus far was creating fashions for our particles and particle system sorts – we haven’t really written a lot SwiftUI code simply but.

In actual fact, it takes only one modifier on TimelineView to begin to deliver this complete factor to life. It’s going to be a little bit of a shortcut as a result of actually it’s simply a place to begin so you possibly can see what our code really does, however that’s okay – we’ll substitute it quickly sufficient.

Add this modifier to the TimelineView now, earlier than the opposite two modifiers:

.gesture(
    DragGesture(minimumDistance: 0)
        .onChanged  drag in
            particleSystem.middle.x = drag.location.x / UIScreen.fundamental.bounds.width
            particleSystem.middle.y = drag.location.y / UIScreen.fundamental.bounds.top
        
)

That tells SwiftUI each time the consumer strikes their finger, we must always replace the X/Y coordinate of our particle system to match their finger’s location.

Discover how we divide the contact location by the width and top of the consumer’s display screen? This issues, as a result of the contact location can be absolute X/Y coordinates, whereas we wish values between 0 and 1.

Go forward and run the app once more and also you’ll see it’s lots higher – you possibly can drag your finger round to attract proper onto the display screen. It’s neat, however we will accomplish that a lot better!

For instance, we might make older strains fade away after 1 second by adjusting the opacity of our drawing context. Put this earlier than the decision to context.draw():

context.opacity = 1 - (timelineDate - particle.creationDate)

That subtracts the particle’s creation date from our timeline date, which can inform us how outdated the particle is. If we then subtract that age from 1, we’ll make older particles fade away. For instance, particles that have been simply made can have an age of 0, so can have an opacity of 1 – 0 or simply 1.

When you run it once more you’ll see the result’s wanting lots higher already – as you drag your finger round it’s such as you’re drawing with mild on the display screen.

However we will do even higher! With one other small change in our drawing code we will inform SwiftUI to mix our graphics collectively in order that overlapping particles get brighter and brighter, as in the event that they have been merging collectively.

Add this simply after the decision to particleSystem.replace():

context.blendMode = .plusLighter

This appears notably efficient once we add some shade to the particles, which is one other one-liner in SwiftUI. Add this after the earlier code:

context.addFilter(.colorMultiply(.inexperienced))

Significantly better!

Our code appears nice thus far, however earlier than we transfer on there’s one vital change we have to make: though we’re making outdated particles fade out, we aren’t really destroying them. Which means SwiftUI is drawing 1000’s of invisible particles, which can chew up a whole lot of RAM and CPU time.

The repair right here is to improve the replace() technique in order that it removes outdated particles as soon as they’re over a second outdated. We are able to do this by subtracting 1 from no matter date was handed in, looping over all of the particles to see which of them have been created earlier than that date, and take away any which can be too outdated.

Put this at the beginning of the replace() technique:

let deathDate = date - 1

for particle in particles 
    if particle.creationDate < deathDate 
        particles.take away(particle)
    

And now our little particle system is completed!

Time for rainbows

After I confirmed my code to my daughter, she mentioned “it’s good, however are you able to make it do rainbows…?” After all we will! In actual fact, it’s solely actually a small adjustment from our present code – slightly than giving all the particle system a uniform shade, we have to give every particle its personal shade, transferring throughout the colour spectrum.

This takes all of 4 steps.

First, we have to add a property to the Particle struct in order that particles have their very own shade:

let hue: Double

Second, we have to add one other hue property, this time to the ParticleSystem class, in order that we will observe the present hue getting used to generate particles – this permits us to maneuver easily by means of the rainbow:

var hue = 0.0

Third, inside replace() we have to create our new particles utilizing the present hue, then modify it upwards a bit. Hues run the vary from 0 by means of 1, so as soon as we transcend that vary we’ll subtract 1 to return to the beginning of the beginning of the spectrum.

Change the present new particle code with this:

let newParticle = Particle(x: middle.x, y: middle.y, hue: hue)
particles.insert(newParticle)
hue += 0.01
if hue > 1  hue -= 1 

And at last, we have to delete the present name to addFilter(), as a result of that provides a single shade. As an alternative, we have to add a singular filter for every particle, which raises an fascinating downside: we’re utilizing context.addFilter() proper now, however how will we take away that filter so as to add a unique one?

The reply is that we will’t: as soon as a filter is added it could actually’t be eliminated. Luckily, SwiftUI’s Canvas makes use of worth semantics, which suggests we will take a duplicate of our context, and any modifications to that context received’t have an effect on the unique context – we will safely apply a filter solely as soon as, slightly than having it have an effect on all particles.

So, substitute your present drawing code with this:

var contextCopy = context
contextCopy.addFilter(.colorMultiply(Coloration(hue: particle.hue, saturation: 1, brightness: 1)))
contextCopy.opacity = 1 - (timelineDate - particle.creationDate)
contextCopy.draw(particleSystem.picture, at: CGPoint(x: xPos, y: yPos))

That creates a shade primarily based on the particle’s present hue, however applies it solely to the context copy in order that it received’t have an effect on future particles – good!

One final tweak

Earlier than we take a shocking flip in a really completely different route, I wish to add yet one more enjoyable tweak to our drawing: slightly than simply drawing every particle as soon as, we will as an alternative draw it 4 occasions at varied positions, making a symmetry impact for our graphics. Once more, this takes little or no work, and appears improbable!

First, we’re going so as to add a property to ContentView that holds an array of flip knowledge – whether or not to flip horizontally or vertically. We’ll have 4 parts in right here, to attract 4 quadrants on our canvas. Add this property now:

let choices: [(flipX: Bool, flipY: Bool)] = [
    (false, false),
    (true, false),
    (false, true),
    (true, true)
]

Now we have to modify our particle-drawing loop once more. To keep away from doing further work, we will copy the context, add the colour filter, and modify the opacity as soon as per particle, however then loop over the choices array to flip the X and Y positions as wanted.

Change your particle loop with this:

for particle in particleSystem.particles 
    var contextCopy = context
    contextCopy.addFilter(.colorMultiply(Coloration(hue: particle.hue, saturation: 1, brightness: 1)))
    contextCopy.opacity = 1 - (timelineDate - particle.creationDate)

    for possibility in choices 
        var xPos = particle.x * measurement.width
        var yPos = particle.y * measurement.top

        if possibility.flipX 
            xPos = measurement.width - xPos
        

        if possibility.flipY 
            yPos = measurement.top - yPos
        

        contextCopy.draw(particleSystem.picture, at: CGPoint(x: xPos, y: yPos))
    

Okay, that’s sufficient futzing round with Canvas – I hope you’ll agree it’s numerous enjoyable! However we’re not finished but…

Who wants palms, anyway?

Up to now that is all fairly customary stuff, but it surely’s time to take this little experiment in a really completely different route. Proper now now we have a bit hack in place that lets us modify the particle places utilizing a finger, however that’s so uninteresting – we will make one thing far more fascinating due to Core Movement!

Core Movement supplies a brilliantly easy API for studying bodily system orientation, all utilizing CMMotionManager. We are able to ask this factor to begin delivering movement updates, and move it a closure to make use of when new movement knowledge is accessible. This could not be run on the primary queue as a result of movement knowledge may be delivered so quick it’d decelerate to your UI.

We don’t wish to pollute our SwiftUI code with any Core Movement work, so we will wrap all of it up in a customized class. To do this, create a brand new Swift file referred to as MotionManager.swift, substitute its Basis import with CoreMotion, then give it this code:

class MotionManager 
    non-public var motionManager = CMMotionManager()
    var pitch = 0.0
    var roll = 0.0
    var yaw = 0.0

That units default values for all three items of movement knowledge we care about, however clearly as quickly because the MotionManager class is created we wish to begin searching for actual knowledge from Core Movement. This may be finished by including an initializer to the category, which can name startDeviceMotionUpdates(), like this:

init() 
    motionManager.startDeviceMotionUpdates(to: OperationQueue())  [weak self] movement, error in
        guard let self = self, let movement = movement else  return 
        self.pitch = movement.perspective.pitch
        self.roll = movement.perspective.roll
        self.yaw = movement.perspective.yaw
    

Now that we begin studying updates, we additionally must cease studying updates when this class is destroyed, so add this too:

deinit 
    motionManager.stopDeviceMotionUpdates()

That’s our new class finished!

Again in ContentView we will put it into motion immediately by including a property to create and retailer a MotionManager object:

@State non-public var motionHandler = MotionManager()

And now we simply must replace the particle system’s middle worth primarily based on the movement handler knowledge – put this after the decision to particleSystem.replace() in your drawing code:

particleSystem.middle = UnitPoint(x: 0.5 + motionHandler.roll, y: 0.5 + motionHandler.pitch)

When you run the app once more you’ll see we will management the motion now simply by tilting the cellphone.

That’s neat, however our story isn’t finished simply but. You see, having to maneuver your hand round all that point may be fairly tiring, as a result of trendy iPhones are simply so darn heavy. How about we make the motion occur with out having to maneuver our hand in any respect?

To do this, go to the Data tab to your goal, then add a brand new key referred to as “Privateness – Movement Utilization Description”. Give it the worth “We have to learn your actions.”

What did that change? Properly, nothing in any respect – but. However now we will make one tiny change to the MotionManager class to do one thing fairly outstanding. Discover this line:

non-public var motionManager = CMMotionManager()

And substitute it with this:

non-public var motionManager = CMHeadphoneMotionManager()

I’ve been sporting AirPods all the time I used to be working, and now you recognize why: once we run the app now iOS will present a permission immediate asking the consumer if the app can learn their movement knowledge, and in the event that they approve it then I can management the entire thing simply be tilting my head!

The place subsequent?

You’ve seen how we will use TimelineView and Canvas to create lovely results, how we will layer on enhancements similar to coloring, mix modes, symmetry and extra, then tie the entire thing into the accelerometers and even AirPods to create one thing fairly outstanding.

However even then, we’ve solely simply scratched the floor of particle methods right here – there’s a lot extra they will do, and naturally SwiftUI is greater than able to delivering all of it. I really made a two-hour livestream going into all kinds of element about particles, making an app the place you possibly can customise each side of the particle system utilizing an interactive UI. Go to https://bit.ly/swiftui-particles to seek out out extra.

Anyway, I promised you one thing enjoyable, lovely, and in contrast to something you’ve seen earlier than, and hopefully you’ll agree it was definitely worth the time – I can’t assume the final time I noticed anybody use AirPods to attract rainbow lights on their cellphone!

Sponsor Hacking with Swift and reach the world’s largest Swift community!



Source link

Share30Tweet19
learningcode_x1mckf

learningcode_x1mckf

Recommended For You

The abstract Vapor service factory design pattern

by learningcode_x1mckf
February 1, 2023
0
The abstract Vapor service factory design pattern

I've written a number of articles about manufacturing unit design patterns on my weblog and this time I might like to speak a couple of particular one, which...

Read more

SwiftNIO tutorial – The echo server

by learningcode_x1mckf
January 27, 2023
0
SwiftNIO tutorial – The echo server

Intoducing SwiftNIO In the event you used a excessive degree net framework, corresponding to Vapor, up to now, you would possibly had some interplay with occasion loops...

Read more

Introducing – Vapor cheatsheet – The.Swift.Dev.

by learningcode_x1mckf
January 23, 2023
0
Introducing – Vapor cheatsheet – The.Swift.Dev.

Out there on Gumroad Thanks for supporting my work by buying the cheatsheet. 🙏 Download now A whole Vapor framework reference for novices. greater than...

Read more

iCloud Programming Tutorial for iOS: An Introduction

by learningcode_x1mckf
January 18, 2023
0
iCloud Programming Tutorial for iOS: An Introduction

Editor’s observe: This week, we work with Ziad Tamim once more to provide you an introduction of iCloud programming. You’ll learn to save and retrieve knowledge from iCloud.On...

Read more

Easy multipart file upload for Swift

by learningcode_x1mckf
January 18, 2023
0
Easy multipart file upload for Swift

I imagine that you've got already heard in regards to the well-known multipart-data add method that everybody likes to add recordsdata and submit type knowledge, but when not,...

Read more
Next Post
Time limit for notify – JavaScript – SitePoint Forums

Other ways to replace a table row? - JavaScript - SitePoint Forums

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Related News

JavaScript Call Stacks: An Introduction

JavaScript Call Stacks: An Introduction

September 26, 2022
Solutions Architect Java JavaScript Cloud – Semi Remote – R850 per hour at e-Merge IT Recruitment

Developer – C / C++ at Parvana Recruitment – Gauteng Johannesburg

January 12, 2023
JavaScript is Everywhere | HackerNoon

JavaScript is Everywhere | HackerNoon

October 7, 2022

Browse by Category

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

RECENT POSTS

  • Java :Full Stack Developer – Western Cape saon_careerjunctionza_state
  • Pay What You Want for this Learn to Code JavaScript Certification Bundle
  • UPB Java Jam brings coffeehouse vibes to Taylor Down Under | Culture

CATEGORIES

  • C#
  • C++
  • Java
  • JavaScript
  • Python
  • Swift

© 2022 Copyright Learning Code

No Result
View All Result
  • Home
  • JavaScript
  • Java
  • Python
  • Swift
  • C++
  • C#

© 2022 Copyright Learning Code

Are you sure want to unlock this post?
Unlock left : 0
Are you sure want to cancel subscription?