How to bring currentview of iCarousel to the top of other views in iCarouselTypeWheel - ios

I want to create a wheel similar to "http://www.asianpaints.com/royale" in iOS, till now I have used iCarousel type: iCarouselTypeWheel. It almost works for me but main problem is the center view got overlapped by next elements. Please let me know how to fix.
Thanks in advance for any help.

Standard wheel set "first" element on most left or most right position, so if you want to carousel begins in top you need set carousel type to iCarouselTypeCustom and provide transform in carousel:itemTransformForOffset:baseTransform: you can take transform from iCarousel source as base

You will actually need to change the z transform value (that is returning) of CATransform3DTranslate function in transformForItemViewWithOffset method. This is kind of hacky though.
To do this,
Open your iCarousel.m file
Look for -(CATransform3D)transformForItemViewWithOffset:(CGFloat)offset method
Inside the switch case statement, go to iCarouselTypeWheel and in the else part, you will need to change the line
return CATransform3DTranslate(transform, 0.0f, -radius, offset * 0.01f);
to
return CATransform3DTranslate(transform, 0.0f, -radius, 0.0f);
Save the file and run the project to see if that gives you the desired result.

Related

Changing UILabel position to strokeEnd of CGPath

I'm trying to achieve something similar to the attached image, where the circle is animated depending on your level progress and then a label is attached to both the end of the animated path to show the gained experience and also the undrawn part of the circle to show the remaining experience. I have the circle animating as wanted but am having trouble coming up with a solution to the labels to appear in the right spot.
I've tried setting the position of the label to the path.currentPoint but that always seems to be the start of the drawn path and not the end.
Any pointers on how to achieve this would be great!
I had been working on your question, first of all to achieve this you must animate the path the real path, not only the strokeEnd, if you animate only the strokeEnd your path.currentPoint will always return the circle endPoint of the path, in order to animate the path you need to make a KeyFramed Animation animating the “path” as keyPath and passing an array of paths from current angle to desired one, then in order to set the correct position for your label you need to get de currentPoint of all this paths values and making another keyFramed animation with “position” keyPath and passing as values all this points collected from paths array
This is a basic example working
The code is in GitHub in this Repo
You have a lot of work to do yet but, this can be an starting point for your final solution
Hope this helps, best regards

Applying animation on a circular chart

I have built a circular graph very similar to this example in paintcode:
http://www.paintcodeapp.com/news/animating-apple-watch-activity-rings-in-paintcode
I have successfully drawn out the control on my iOS view with ease, but now I would like to animate the graph so that it begins at 0 and eases towards the specified angle. Basically the animation should look like the first two seconds of the video in the URL above.
What is the best way to go about this type of animation?
FYI: I am working in C#/Xamarin but I am not fussy on syntax at all, so an Objective C or Swift example will do just fine.
I wrote a UIView subclass which does exactly what you're asking for. You just provide an array of rings along with a few other parameters, and it handles all of the setup and management for you.
https://github.com/lionheart/ConcentricProgressRingView
At the top of your UIViewController, import the module:
import ConcentricProgressRingView
Then, in your viewDidLoad:
let rings = [
ProgressRing(color: UIColor.redColor()),
ProgressRing(color: UIColor.blueColor()),
]
let margin: CGFloat = 2
let radius: CGFloat = 80
let progressRingView = ConcentricProgressRingView(center: view.center, radius: radius, margin: margin, rings: rings, defaultWidth: 20)
view.addSubview(progressRingView)
Once you've instantiated your ConcentricProgressRingView instance, animate a specific ring to a percentage with setProgress.
ring.arcs[1].setProgress(0.5, duration: 2)
Under the hood, this just uses CABasicAnimation and sets a few parameters which make it look "right". I know you're not using Swift, but if you want specific pointers, just check out the source code to see how I've solved it. Hope this helps!
You want to use CAShapeLayers, one for each arc, with a full-circle arc installed in each one, and the line cap style set to rounded.
Then you want to set the strokeEnd property to a small value (try .05). That will cause the shape to only draw 5% of it's arc.
Finally you want to submit CAAnimations to each shape layer that animate the strokeEnd value from the starting value to 1.0. That will animate the arcs drawing the full circle.
I don't have any ready-made sample code for you. I do have a sample project written in Objective-C that uses a very similar technique to create a "clock wipe" animation using a CAShapeLayer as a mask layer.
Take a look at the clock wipe animation in the project "iOS CAAnimationGroup demo" project on Github.

CATransform3D - understanding the transform values

The picture shows a simple UIView after applying the following transform:
- (CATransform3D) transformForOpenedMenu
{
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1.0 /450;
transform = CATransform3DRotate(transform, D2R(40), 0, 1, 0);
transform = CATransform3DTranslate(transform, 210, 150, -500);
return transform;
}
I'm trying to make the distances highlighted with black to have equal length. Could you please help me understand the logic behind the values and calculations?
Cheers
UPD Sept 13
Looks like removing 3DTranslate keeps distances equal. I see I can use layer's frame property to reposition rotated view to the bottom left of the screen. Not yet sure, but this might actually work.
The .m34 value you are setting is best set on the sublayerTransform of the containing view rather than the view you are transforming.
I don't fully understand the maths behind affine transforms so I made this project which allows me to play around with the transform values to achieve the effect I want. You can plug in the values from your code above and see what it looks like, though note that there is already a perspective value applied using the sublayerTransform property mentioned above.
For your specific case, I think you want to adjust the anchor point of the layer to (0.0,0.5) and apply the rotation transform only. This assumes you want the menu to swing back like a door, with the hinges on the left edge.
The problem you're seeing is caused by your CATransform3DTranslate call. You're essentially setting the Y Axis off center, and hence seeing a different perspective view of the frame.
Think of it this way;
You're standing in the center of a long narrow field stretching off into the horizon. The edge of the field appears as if it is converges to a center point somewhere off in the distance. The angle of each edge to the converging point will appear equal if you are at the center of the field. If, on the other hand, you move either to the left or the right, the angles change and one will seem greater than the other (inversely opposite of course).
This is essentially what is happening with your view; As your converging points are to the right, changing the Y axis away from 0 will have the same effect as moving to the left or right in my example above. You're no longer looking at the parallel lines from the center.
so in your code above Setting the ty in CATransform3DTranslate to 0 Should fix your problem I.E.
transform = CATransform3DTranslate(transform, 210, 0, -500);
You may also need to alter the tz and tx Value to make it fit.
OK, so what eventually solved my question is this:
3D transform on Y axis to swing the view like a door transform = CATransform3DRotate(transform, D2R(40), 0, 1, 0);
set Z anchor point on a layer, to move it back targetView.layer.anchorPointZ = 850;
adjust layer position so that the view is located slightly to the bottom left of the parent view:
newPosition.x += 135 * positionDirection;
newPosition.y += 70 * positionDirection;
This sequence adjusts position without CATransform3DTranslate and keeps the 'swinged' effect not distorted.
Thanks everybody!

iOS - Core Animation - How to set origin when transform property is changed

I have a UIView added as a subview inside its parent view. When the user taps on this UIView, I want it to be transformed so it takes the entire screen.
I am trying to do so by changing its transform property and I was able to make its size match its parent view but not its position.
I read the Apple documentation and found frame cannot be used with transform. How do I make its position line up with its parent view?
Also, without using transform, is it a good idea to do so by changing its constraints, e.g., NSLayoutAttributeHeight?
i think you can simply change the frame after you scale it by transform like this:
//i am using 5 for testing purpose
self.backgroundImageView.transform = CGAffineTransformMakeScale(5, 5);
self.backgroundImageView.frame = CGRectMake(0, 0, self.backgroundImageView.frame.size.width, self.backgroundImageView.frame.size.height);
OR change the anchor point before the scale:
self.backgroundImageView.layer.anchorPoint = CGPointMake(0.5, 0.5);
self.backgroundImageView.transform = CGAffineTransformMakeScale(5, 5);
Update:
if you use Autolayout in your UIView, the solution might depends on how you set up your constraints, For example, if you set up your image constraints like this:
I would suggest still using the scale transform, but change the top and left constraint to 0.
or you set up your image constraint like this:
I would change the center alignment x and y to the center point of the super view
Maybe you can let me know how you set up you constraints, i can do some test for you on my local.

Cocoa Touch, set transform identity equal to current transform state

How do I change the transformIdentity. How do we set the transform "zero", or alter the transformIdentity of a view to the current state of the transform of said view.
In other words, I want to scale a view, and then set the current state (say scale of 2.5) to the default scale of the view (scale of 1).
example code:
view.transform = CGAffineTransformMakeScale(1, 2.5);
pseudo code for what I want to do:
view.transform = setTransformIdentityTo:view.currentState;
If I understand correctly transformIdentity is the state at which a scale would be 1, or a rotation would be zero, the default "zero" transform.
NOTE: The reason I want to do this is so that I can set a negative scale transform on only one axis of the view and alway get a flipped view relative to the last state of the view before the flip was invoked.
CGAffineTransformIdentity does reset a view or layer to its original, untransformed state, and thus cannot be redefined.
But if you want your "personal" reset transform, e.g. with a different scale, why don't you simply define it, e.g. by using CGAffineTransform myCGAffineTransformIdentity = CGAffineTransform CGAffineTransformMakeScale (sx,sy);, and apply it to your views?
selectedSticker.transform = CGAffineTransformIdentity;
Seems like this works for what I want to do:
CGAffineTransform trans = CGAffineTransformMakeScale(1, 2.5);
view.transform = CGAffineTransformConcat(selectedSticker.transform, trans);
What you can do, if you want to have your view still be transformed even when its own transform is the identity transform, is to put it inside another view that is transformed. Give the outer view the transform you want for your default; the inner view is the one where you do the actual work.
So, let's say you want your “identity” to be scaled by 2× horizontally. You set your outer view's transform to that transform, and leave it that way, and leave the inner view untransformed. When you want to add further transformation, you add it to the inner view, and when you want to reset it back to your default, you set the inner view's transform back to (true) identity. Your inner view will still scaled (or whatever) by the outer view's transform.
Note: I tried this briefly and found that (a) Auto Layout barfed on it, (b) scaling is outward from the anchor point, which means my outer view's horizontal scale pushed my inner view a little bit out-of-bounds, and (c) changing the outer view's anchor point to CGPointZero produced even further hilarity.
So, while this is theoretically a nice, simple, elegant solution, it may actually be more problematic than it's worth, in which case I recommend what Reinhard Männer suggested.

Resources