Filling animation + permanent mask in swift? - swift

Something like this:
Or follow this link if you can:
https://www.lottiefiles.com/450-play-fill-loader
My case is simpler - I have a view with mask which consists of multiple rects and a linear fill. Mask is created in runtime (so I can't use lottie) but remains permanent, fill is animated (filling from right to left). But how to draw it?
Note: I tried to find a similar animation implementation but in most cases they just try to change the mask params while in my case mask is constant.

If i understand you correct, you can add subview under your mask and change its width, something like that:
let fillingStartFrame = CGRect.init(x: 0, y: 0, width: 0, height: view.frame.height)
let fillingEndFrame = CGRect.init(x: 0, y: 0, width: view.frame.width, height: view.frame.width)
let fillingView = UIView(frame: fillingStartFrame)
view.insertSubview(fillingView, belowSubview: YOUR_MASK)
UIView.animate(withDuration: 0.3) {
fillingView.frame = fillingEndFrame
}

Related

UIView does not change colors after setting backgroundColor programmatically

I'm creating a subview to add onto an existing view. I'm trying to assign the background color to this subview to red programmatically but displays as the defaulted color still.
let toggleView = UIView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight))
toggleView.backgroundColor = UIColor.red
As from the above code snippet it looks like that you are creating a view but not adding it as a subview to the parent view. It will be best if you can provide full function so that we can look into the actual cause.
You have created the toggleView with a red background but you have not added it to the main view, this is what you should do:
let screenWidth = view.frame.size.width
let screenHeight = view.frame.size.height
let toggleView = UIView(frame: CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight))
toggleView.backgroundColor = UIColor.red
view.addSubview(toggleView)

Swift: Constrain object to and point within a circular NSLayoutConstraint? Circle anchor?

Within a view controller, I have a UIView (backgroundCircle) and a UILabel. The UILabel is moving based on motionManager.startAccelerometerUpdates data. Using this data, I am animating the label.center point. However, I want to bound the movement so that it only moves within a circular area around its starting point (as if it was on a leash from the starting point). Is this possible? Sample code below if it helps.
//objects called during set up
let backgroundCircle = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
backgroundCircle.layer.cornerRadius = circle.frame.width / 2
backgroundCircle.backgroundColor = .clear
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
label.text = "Hello"
//this is called in a separate function, continuously
label.center = newCenter
I'd try using UIKit Dynamics, especially UICollisionBehavior which allows to define collision boundaries with bezier paths.

Swift how to animate fixed uiview from right to left

I'm trying to make my view animate from right to left & change colour unlike the example from dribbble. Currently, my view is moving around the screen not staying fixed to the original position. I'm trying to make it look like this example here but can't seem to make it animate from right to left properly. https://dribbble.com/shots/5690048-Social-Meet-Up-UI-Kit
let options = UIViewAnimationOptions.autoreverse.rawValue | UIViewAnimationOptions.repeat.rawValue | UIViewAnimationOptions.curveEaseInOut.rawValue
UIView.animate(withDuration: 1.0, delay: 0.0, options: UIViewAnimationOptions(rawValue: options), animations: {
// any changes entered in this block will be animated
self.goingBackgrondView.backgroundColor = UIColor.red
self.goingBackgrondView.frame = CGRect(x: 320-50, y: 0, width: 50, height: 55)
}, completion: nil)
That is not move animation. That is resize animation.
Change this:
self.goingBackgrondView.frame = CGRect(x: 320-50, y: 0, width: 50, height: 55)
to this:
self.goingBackgrondView.frame = CGRect(x: 320, y: 0, width: 0, height: 55)

Is it possible to create an instance of a particular location on the screen (using auto layout)?

I have 4 playing cards on the screen. At the press of a button, I want one of the cards at random to move to the middle of the top half of the screen. Is it possible to create an instance of a constraint (eg: centerXAnchor with constant 0, and centerYAnchor with constant -200) so that I can use CGAffineTransform and move the random image to this point?
Ive tried creating an instance of a CGRect Frame:
let destination = CGPoint(x: 10, y: 10)
but this does not move evenly across devices.
An affine transformation matrix is used to rotate, scale, translate, or skew the objects you draw in a graphics context.
I don't think CGAffineTransform is the ideal thing to use for this task. You aren't doing any the above things (rotate, scale, translate, or skew).
I think you would likely be best using UIView.animateWithDuration
let cardSize = CGSize(width: 100, height: 100)
let card = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: cardSize))
UIView.animate(withDuration: 1.0) {
card.frame = CGRect(origin: CGPoint(x: 100, y: 100), size: cardSize)
}

Creating transparent gradient and use it as an alpha-mask in SpriteKit

I am trying to make a gradient and use it as an alpha mask. Right now, I am able to make an image similar to this (from black to transparent):
This is the code which I use to make all this:
private func createImage(width: CGFloat, height: CGFloat) -> CGImageRef?{
if let ciFilter = CIFilter(name: "CILinearGradient"){
let ciContext = CIContext()
ciFilter.setDefaults()
let startColor = CIColor(red: 0, green: 0, blue: 0, alpha: 0)
let endColor = CIColor(red: 0, green: 0, blue: 0, alpha: 1)
let startVector = CIVector(x: 0, y: height-10)
let endVector = CIVector(x: 0, y: height-22)
ciFilter.setValue(startColor, forKey: "inputColor0")
ciFilter.setValue(endColor, forKey: "inputColor1")
ciFilter.setValue(startVector, forKey: "inputPoint0")
ciFilter.setValue(endVector, forKey: "inputPoint1")
if let outputImage = ciFilter.outputImage {
let cgImage:CGImageRef = ciContext.createCGImage(outputImage, fromRect: CGRect(x: 0, y: 0, width: width, height: height))
return cgImage
}
}
return nil
}
For me, this way works pretty much perfect because I create a mask only once in my code (when GUI element is created) so performance is not affected at all.
The thing is that I actually need a resulting image (gradient texture) to look like this (gradients should have fixed height, but size of black area may vary):
Because CILinearGradient filter doesn't have an inputImage parameter, I can't chain two filters one after another. But rather I have to create an image, and apply a filter, then to create another image, and apply a new filter.
I solved this just like described above. Also I had to extend a process in three steps (create upper gradient, create middle, black area and create lower gradient) and then to combine all that into one image.
I wonder is there anything about CILinearGradient filter I am not aware of ? Maybe there is an easier way to attack this problem and make a complex gradient?
Note that I am using SpriteKit and solving this with CAGradientLayer is not a way I want to go.
I'm not sure Core Image is the right tool for the job here — you'd probably be better off using CGGradient functions to draw your image.
Aside: What you're asking would be possible but cumbersome in CoreImage. Think of it by analogy to what you'd do to create these images as a user of Photoshop or other common graphics software... the CoreImage way would be something like this:
Create a layer for the upper gradient, fill the entire canvas with your gradient, and transform the layer to the proper position and size
Create a layer for the middle black portion, fill the entire canvas with black, and transform the layer to the proper position and size
Ditto #1, but for the bottom gradient.
You could do this with a combination of generator filters, transform filters, and compositing filters...
gradient -> transform -\
}-> composite -\
solid color -> transform -/ \
}-> composite
gradient -> transform ————————————————--/
But setting that up would be ugly.
Likewise, in Photoshop you could use selection tools and fill/gradient tools to create your gradient-fill-gradient design completely within a single layer... that's what drawing a single image using CoreGraphics is analogous to.
So, how to do that single-pass CG drawing? Something like this...
func createImage(width: CGFloat, _ height: CGFloat) -> CGImage {
let gradientOffset: CGFloat = 10 // white at bottom and top before/after gradient
let gradientLength: CGFloat = 12 // height of gradient region
// create colors and gradient for drawing with
let space = CGColorSpaceCreateDeviceRGB()
let startColor = UIColor.whiteColor().CGColor
let endColor = UIColor.blackColor().CGColor
let colors: CFArray = [startColor, endColor]
let gradient = CGGradientCreateWithColors(space, colors, [0,1])
// start an image context
UIGraphicsBeginImageContext(CGSize(width: width, height: height))
defer { UIGraphicsEndImageContext() }
let context = UIGraphicsGetCurrentContext()
// fill the whole thing with white (that'll show through at the ends when done)
CGContextSetFillColorWithColor(context, startColor)
CGContextFillRect(context, CGRect(x: 0, y: 0, width: width, height: height))
// draw top gradient
CGContextDrawLinearGradient(context, gradient, CGPoint(x: 0, y: gradientOffset), CGPoint(x: 0, y: gradientOffset + gradientLength), [])
// fill solid black middle
CGContextSetFillColorWithColor(context, endColor)
CGContextFillRect(context, CGRect(x: 0, y: gradientOffset + gradientLength, width: width, height: height - 2 * gradientOffset - 2 * gradientLength))
// draw bottom gradient
CGContextDrawLinearGradient(context, gradient, CGPoint(x: 0, y: height - gradientOffset), CGPoint(x: 0, y: height - gradientOffset - gradientLength), [])
return CGBitmapContextCreateImage(context)!
}