I have viewController in storyboard. And 4 squares. I want to place my squares on view. At first I want to show two squares. If I press on button I want to my red 2 squares move left and I show next 2 blue squares. Like this animation.
Do I need to create a scrollView or collectionView or something else to move the squares? What the best way to create this?
Use UiVew.animate and CGAffineTransform
Setup
Create a UIView that will contain your squares. Make sure that UIView has clip to bounds enabled. Then, add your four squares in, with the blue ones nested inside the UIView, but with their coordinates outside of the container so that they're getting clipped.
Then, when you want to move them, you simply translate all of your squares x to the left. By putting this movement inside of a UIView.animate block, iOS will perform the animation for you.
UIView.animate(withDuration: 2.0) {
self.redTopView.transform = CGAffineTransform(translationX: -160, y: 0)
self.redBottomView.transform = CGAffineTransform(translationX: -160, y: 0)
self.blueTopView.transform = CGAffineTransform(translationX: -160, y: 0)
self.blueBottomView.transform = CGAffineTransform(translationX: -160, y: 0)
}
Final result: http://g.recordit.co/SToMSZ77wu.gif
Related
What I'm trying to Achieve
I am trying to implement the gradient bubble effect in Swift iOS, where the chat bubbles towards the top are a lighter color and the chat bubbles towards the bottom are a darker color, and when you scroll a bubble you see the gradient change.
The link below is an example of the iMessage gradient effect.
An example image of the iMessage gradients
What I've Tried
I created a view and added a gradient layer:
let gradient = CAGradientLayer()
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.endPoint = CGPoint(x: 0, y: 1)
gradient.colors = [UIColor.systemBlue.cgColor, UIColor.systemPink.cgColor]
gradient.frame = UIScreen.main.bounds
view.layer.addSublayer(gradient)
view.backgroundColor = .yellow
I created a view and used it as a mask
mask.backgroundColor = .yellow
mask.alpha = 1
mask.frame.size = CGSize(width: 100, height: 100)
mask.center = view.center
view.mask = mask
The result is like this Gif below:
Example of my progress using a Mask View
I was originally hoping to add a gradient to a UICollectionView and have the UICollectionViewCells mask the gradient to achieve the above iMessage gradient effect, but then I realized I can only apply one mask to a UIView (not multiple), so I'm stuck on using this approach.
Other Ideas
My other ideas were to apply a gradient to each UICollectionViewCell and determine the gradient offset of each UICollectionViewCell manually based on the location of the cell, however, my main concern is this is not going to have good performance.
Please Help
I was hoping to see if anyone could outline a general method or link to how to achieve the iMessage gradient background effect?
I understand this is a more general question and often times general questions are "bad" questions for stack overflow, but I'm really stuck on this problem and would incredibly appreciate any help or advice for achieving this effect!
Thank you for your time!
Solution Figured Out
Wow, this is sketchy/hacky to implement. Below is a GitHub Gist of how its done.
https://gist.github.com/josharnoldjosh/e04d41f10de6ab378da931ab11370d11
The way it works is you set a background to the gradient, then, you mask each individual cell, "cutting out" a hole in the cell to be transparent. The rest of the cell must be white to simulate the background being white.
There are multiple parts to this. First, you need to set up a gradient that is top-to-bottom. Your current gradient goes from the top right to the bottom left.
Change these 2 lines:
gradient.startPoint = CGPoint(x: 1, y: 0)
gradient.endPoint = CGPoint(x: 0, y: 1)
To
gradient.startPoint = CGPoint(x: 0.5, y: 0)
gradient.endPoint = CGPoint(x: 0.5, y: 1)
Next is the issue of how to make the view take on the gradient color tied to the screen, as you scroll the table view/collection view. That is not easy, and I'm not sure how you'd do that. I would probably have to attach code to the table view/collection view's UIScrollViewDelegate and implement the scrollViewDidScroll(_:) method, figure out the change in scroll view offset, and shift the gradient layer to match the scroll offset.
I have two views, videoView (which is the mainView) and subVideoView (which is the subView of mainView).
I am trying to minimize both views using animation at the same time, as shown in the below code. I am able to minimize the videoView (i.e mainView) and not the subVideoView.
However, when I hide code for minimising videoView (i.e mainView), I am able to minimise the subVideoView.
I believe it has to do something with the way I am animating.
Can some one please advice how I can minimise both views (proportionally) with animation at the same time and end up with the below result.
RESULTS OF EXISTING CODE
func minimiseOrMaximiseViews(animationType: String){
UIView.animate(withDuration: 0.5, delay: 0, options: [],
animations: { [unowned self] in
switch animationType {
case "minimiseView" :
// Minimising subVideoView
self.subVideoView.frame = CGRect(x: self.mainScreenWidth - self.minSubVideoViewWidth - self.padding,
y: self.mainScreenHeight - self.minSubVideoViewHeight - self.padding,
width: self.minSubVideoViewWidth,
height: self.minSubVideoViewHeight)
// Minimising self i.e videoView
self.frame = CGRect(x: self.mainScreenWidth - self.videoViewWidth - self.padding,
y: self.mainScreenHeight - self.videoViewHeight - self.padding,
width: self.videoViewWidth,
height: self.videoViewHeight)
self.layoutIfNeeded()
case "maximiseView":
// Maximising videoView
self.frame = CGRect(x: 0, y: 0, width: self.mainScreenSize.width, height: self.mainScreenSize.height)
// Maximising subVideoView
self.subVideoView.frame = CGRect(x: self.mainScreenWidth - self.maxSubVideoViewWidth - self.padding,
y: self.mainScreenHeight - self.maxSubVideoViewHeight - self.padding - self.buttonStackViewBottomPadding - buttonStackViewHeight,
width: self.maxSubVideoViewWidth,
height: self.maxSubVideoViewHeight)
default:
break
}
As per your requirement add subViews in mainView and set
mainView.clipsToBounds = true
and In the minimiseOrMaximiseViews method you just need to manage Y axis of mainView for show and hide.
I could minimise both views simultaneously using animation, when I've set both views (videoView and subVideoView) as subViews of window i.e UIApplication.shared.keywindow.
Unless I'm missing something, this kind of animation is much more easily achieved by animating the transform property of the videoView (the main view). You will need to concatenate scaling and translation transforms for that, because scaling is by default applied to a view's center.
The trick is that applying a transform to a view affects all its subviews automatically, e.g. applying a scaling transform to a view scales both the view and all its subviews.
To make a reverse animation just set videoView.transform = CGAffineTransformIdentity inside an animation block.
Caveat: You should be careful, though, with the frame property of a transformed view. The frame property is synthetic and is derived from bounds, center and transform. This means that setting a view's frame resets its transform property. In other words, if you manipulate your views using transform, you most likely want to avoid setting their frames directly.
I have the following code, which basically just grows a UIImage from 0 to its intended height:
let bounds = self.tankLevelRep.bounds
UIView.transition(with: self.tankLevelRep, duration: 2.0, animations: {self.tankLevelRep.bounds = CGRect(x: bounds.origin.x, y: bounds.origin.y, width: bounds.size.width, height: 118.0)}, completion: nil)
This works fine except for the fact that it grows from the middle. I would like it to grow upwards from the bottom. The attached graphic shows the animation as it nears its end. You can see that the animation is not starting from the bottom, but rather, from the center, and growing outwards. I would like for it to start at the bottom, and the image to grow upwards.
What can I change in my code to make this happen?
Notes:
• "tankLevelRep" is the outlet name for my UIImage
You should update the frame and the bounds mirror the change, in your code make sure you animate from the bottom by setting the origin to bounds.size.height instead of the origin.y. Here is a nice fill animation from another post.
Im having a really stupid problem with a UIView. I create the UIView programatically and then make it a circle by making the corner radius half the height of the view looks like this:
But when I try to animate the circle to a different location it becomes all deformed like this:
Here is the code I am using to animate the view just a simple UIView.animateWithDuration:
UIView.animateWithDuration(2, delay: 0, options: UIViewAnimationOptions.AllowUserInteraction, animations: {
self.secondDot.frame.origin = CGPoint(x: self.view.frame.size.width - self.secondDot.frame.size.width, y: 0)
self.secondDot.frame.size = self.secondDot.frame.size
}, completion: nil)
I should also add that the circle doesn't always become deformed but more often than not it does. Please help any suggestions would be much appreciated.
EDIT: I should mention that the circle is in motion already before the
UIView.animationWithDuration occurs I don't know if that could be a
problem
What is the point of this line?
self.secondDot.frame.size = self.secondDot.frame.size
You either
want to resize the circle uniformly while moving
or not.
If case 1) the the newSize.width != newSize.height...but thats a problem because your circle is not a "square" anymore behind the scenes.
If case 2) just remove the lines.
I had exactely the same situation.
After some research, I've found comment in header file for setFrame method
// animatable. do not use frame if view is transformed since it will
not correctly reflect the actual location of the view. use bounds +
center instead.
I have decided to use setCenter instead of setFrame which solved the distortion issue.
I am trying to design a Swift game but I noticed that I am unable to have the ball in the game go into the top left corner of the screen. I set my anchor point below.
override func didMoveToView(view: SKView) {
anchorPoint = CGPoint(x: 0, y: 0.5)
}
I noticed that if I change the anchor point's y-coordinate to 0, I am only able to access the right half of the screen and there's an invisible wall on the left half.
What is the reason for this?
This code is a bit confusing. is the Sprite called anchorPoint? If you can't get the position right, try setting the Sprite to the centre of the screen first, then change the x and y positions gradually so you can experiment with where the Sprite ends up on the screen. Set it to the center like this:
spriteName.position = CGPoint(x: size.width * 0.5, y: size.height * 0.5)