Page rendering using leaves view - objective-c

I am using the Leaves project to move from one pdf page to another it is working fine. Now the page curl is from left to right and from right to left like a note book. I need to curl it from top to bottom and bottom to top. Does anyone know the methods to do it, or any other templates to do this?

Just use the UIPageViewController from Apple instead of Leaves http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIPageViewControllerClassReferenceClassRef/UIPageViewControllerClassReference.html
This gives you exactly the curl they use in iBooks for example, so a lot more beautiful (more realistic) matrix of the curled view.
Just make all your note sheets a View and give the views the UIPageViewController
It also has various options where the spine is located and other stuff
To make a curl up you could either use
[UIView setAnimationTransition:UIViewAnimationTransitionCurlUp forView:self.view cache:YES];
[UIView setAnimationTransition:UIViewAnimationTransitionCurlDown forView:self.view cache:YES]; like bala mentioned or instantiate another UIPageViewController that is responsible for up and downwards transitions.
If you want to really go into the view and maybe switch with this through multiple items i would just use 2 uipageviewcontrollers so one horizontal and one vertical

this method
SampleViewController *sampleView = [[[SampleViewController alloc] init] autorelease];
[sampleView setModalTransitionStyle:UIModalTransitionStylePartialCurl];
[self presentModalViewController:sampleView animated:YES];
as in this tutorial may be helpful

The existing leaves view is hard-coded to be left-to-right. You can create a hard-coded top-to-bottom version easily enough, but there may well be more robust generic solutions.
However, if you definitely must use leaves, you can rewrite its source to suit. The solution below hard-codes for vertical motion. If you need horizontal and vertical options, you will have more work do do.
Display
setUpLayers
Reverse the axes in every call to CGPointMake. This fixes up the gradients that display the shadows.
Also change topPage.contentsGravity = kCAGravityLeft; to topPage.contentsGravity = kCAGravityBottom; and topPageReverseImage.contentsGravity = kCAGravityRight; to topPageReverseImage.contentsGravity = kCAGravityTop;. This is a subtle change that makes sure incoming pages are displayed with the correct edges.
setLayerFrames
Most of the geometry changes happen here. The original code oddly uses view bounds in some places and layer bounds in others. I duplicated those, but it would be good to understand if the distinction is meaningful before using this code in production.
- (void) setLayerFrames {
topPage.frame = CGRectMake(self.layer.bounds.origin.x,
self.layer.bounds.origin.y,
self.bounds.size.width,
leafEdge * self.layer.bounds.size.height);
topPageReverse.frame = CGRectMake(self.layer.bounds.origin.x,
self.layer.bounds.origin.y + (2*leafEdge-1) * self.layer.bounds.size.height,
self.bounds.size.width,
(1-leafEdge) * self.layer.bounds.size.height);
bottomPage.frame = self.layer.bounds;
topPageShadow.frame = CGRectMake(0,
topPageReverse.frame.origin.y - 40,
bottomPage.bounds.size.width,
40);
topPageReverseImage.frame = topPageReverse.bounds;
topPageReverseImage.transform = CATransform3DMakeScale(1, -1, 1);
topPageReverseOverlay.frame = topPageReverse.bounds;
topPageReverseShading.frame = CGRectMake(0,
topPageReverse.bounds.size.height - 50,
topPageReverse.bounds.size.width,
50 + 1);
bottomPageShadow.frame = CGRectMake(0,
leafEdge * self.bounds.size.height,
bottomPage.bounds.size.width,
40);
topPageOverlay.frame = topPage.bounds;
}
Interaction
updateTargetRects
Where this calculates nextPageRect and prevPageRect, put them on the bottom and top instead of the right and left.
touchesMoved:withEvent:
Change self.leafEdge = touchPoint.x / self.bounds.size.width; to self.leafEdge = touchPoint.y / self.bounds.size.height;.

Related

Change height of a NSProgressIndicator programmatically

I cannot find a way to set the height of a NSProgressIndicator programmatically.
My try so far:
NSProgressIndicator *ind = [[NSProgressIndicator alloc] init];
[ind setStyle: NSProgressIndicatorBarStyle];
// Height does not change height of the actual indicator
[ind setFrame: NSMakeRect(0, 0, 100, 50)];
[ind setBounds: NSMakeRect(0, 0, 100, 50)];
//[ind setControlSize: 0]; does only make it smaller, not bigger
[view addSubview: ind];
I found NSProgressIndicatorBarStyle enumeration in the documentation, but I couldn't find a method to specify the thickness.
Here a screenshot describing my problem: (layer has a background of red for better understanding):
This also occurs when using the NSButton class. Is there a workaround for this?
in iOS You can't change the progress indicator height just changing its frame, due to framework restrictions. However you should be able to achieve the same result, playing with transform
_indicator.transform = CGAffineTransformMakeScale(1.0f, 0.6f);
EDIT: I just tried on Mac OS X
_indicator.layer.transform = CATransform3DMakeScale(1.0f, 0.6f, 0.0f);
and it doesn't work, so it is not like iOS, likely because of how it is implemented on Cocoa (like Ken suggested).
The only way I managed to change the height is using controlSize, but I don't think it will suit your needs (since it doesn't allow you to specify points).
[_indicator setControlSize:NSMiniControlSize]; // or NSSmallControlSize
You should be able to use an arbitrary frame by subclassing NSProgressIndicator, and overriding drawRect, at this point my recommendation would be to look around to find something that can be extended for your use, like this one
https://www.cocoacontrols.com/controls/lbprogressbar
in IB you can
select your NSProgressIndicator control
in the utilities view select the View Effects inspector
press + in Content Filters
select Lanczos Scale Transform filter
set the appropriate scale value in the Scale row
set the Aspect Ratio too if you need to change the height only
this can be added programmatically also, just google for it how to add Content Filters to NSView

How to apply top offset for status bar in iOS7 for UITabBarController children only

My app is a UITabBarController based app with 3 children vc. The status bar is overlapping with my viewcontrollers. I have tried everything I have found in SO:
I tried adding this to my AppDelegate:
//Offset downwards to make room for status bar
self.window.bounds = CGRectMake(0, -20, self.window.frame.size.width, self.window.frame.size.height);
It worked because it moved the entire UITabBarController down by 20. But this resulted in the tabs being cut off at the bottom by -20.
So Im thinking i just want to apply the effect to the UIViewControllers in each tab since there is not much to cut off at the bottom.
So I tried adding similar code to the viewDidLoad and calling setNeedsDisplay afterwards:
self.view.bounds = CGRectMake(0, -20, self.view.frame.size.width, self.view.frame.size.height);
But this has no effect at all, still get overlapping.
I tried adding this to viewDidLoad
self.edgesForExtendedLayout = UIRectEdgeNone;
and I also unchecked the Extend Edges Under Top Bars but it still runs into the status bar. Also tried:
if ([self respondsToSelector:#selector(edgesForExtendedLayout)])
{
self.edgesForExtendedLayout = UIRectEdgeNone;
}
but no cigar.
I dont have the deltas because im using AL.
I dont want to HIDE my status bar because it displays open/close times so the user would find it handy to be able to see the time.
Im thinking shifting the view down a bit in each viewcontroller that needs it is the best option, but why didnt my code work for #2?
(btw:not sure why code format isnt working and the numbering is off, even when i try to edit it, it seems to be working fine in the edit view but not in the pre-view or live-view)
for iOS 7 and xcode 5 to avoid overlapping
use in
viewDidLoad method
self.edgesForExtendedLayout = UIRectEdgeNone;
self.automaticallyAdjustsScrollViewInsets = NO;
Well, I have the same problem as you.
I fixed it with many moves.
I use ruby motion:
#window.bounds = CGRectMake(0, -20, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height - 20)
#window.frame = CGRectMake(0, 0, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height)
....
tabBarController.view.bounds = CGRectMake(0, 20, UIScreen.mainScreen.bounds.size.width, UIScreen.mainScreen.bounds.size.height - 20)
Let me know how it's going at your end.
update:
attach the screenshot:

CGAffineTransformMakeRotation not working well

I have a game where you have "specks" and "connectors".
I make a UIImageView that connects two "specks"… but as you can see (if the picture works), when the specks are almost on top of each other, like on the left, it doesn't work. This is my code… and I was wondering how you would make the rotation more accurate in that sort of situation.
UIImageView *connector = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"RedConnector.png"]];
connector.contentMode = UIViewContentModeScaleToFill;
connector.hidden = NO;
connector.alpha = 1;
connector.frame = CGRectMake(0, 0, [self DistanceBetweenPoints:speck1.center :speck2.center], 7);
connector.center = CGPointMake((speck1.center.x + speck2.center.x)/2, (speck1.center.y + speck2.center.y)/2);
//This is where the prob is:
connector.transform = CGAffineTransformMakeRotation(tanh((speck2.center.y - speck1.center.y)/(speck2.center.x - speck1.center.x)) );
[Connectors addObject:connector];
[self.view addSubview:connector];
"DistanceBetweenPoints" is not the problem. That works fine. Also, I know the center AND the frame is sort of redundant-- I'll work that out later. Third of all, I have made sure that two specks are never directly on top of each other (to never divide by zero). Last of all, I STILL WANT THESE TO BE UIIMAGEVIEWS!! So I can change the image and animate them so it looks like electricity flowing from speck to speck.
If anybody could show me how to make the rotation more accurate, it would be greatly appreciated. Thanks!
You should use atan2(y,x), not tanh(y/x). That is, you want the arctangent, not the hyperbolic tangent.

How to flip a UIView around the x-axis while simultaneously switching subviews

This question has been asked before but in a slightly different way and I was unable to get any of the answers to work the way I wanted, so I am hoping somebody with great Core Animation skills can help me out.
I have a set of cards on a table. As the user swipes up or down the set of cards move up and down the table. There are 4 cards visible on the screen at any given time, but only the second card is showing its face. As the user swipes the second card flips back onto its face and the next card (depending on the swipe direction) lands in it's place showing its face.
I have set up my card view class like this:
#interface WLCard : UIView {
UIView *_frontView;
UIView *_backView;
BOOL flipped;
}
And I have tried flipping the card using this piece of code:
- (void) flipCard {
[self.flipTimer invalidate];
if (flipped){
return;
}
id animationsBlock = ^{
self.backView.alpha = 1.0f;
self.frontView.alpha = 0.0f;
[self bringSubviewToFront:self.frontView];
flipped = YES;
CALayer *layer = self.layer;
CATransform3D rotationAndPerspectiveTransform = CATransform3DIdentity;
rotationAndPerspectiveTransform.m34 = 1.0 / 500;
rotationAndPerspectiveTransform = CATransform3DRotate(rotationAndPerspectiveTransform, M_PI, 1.0f, 0.0f, 0.0f);
layer.transform = rotationAndPerspectiveTransform;
};
[UIView animateWithDuration:0.25
delay:0.0
options: UIViewAnimationCurveEaseInOut
animations:animationsBlock
completion:nil];
}
This code works but it has the following problems with it that I can't seem to figure out:
Only half of the card across the x-axis is animated.
Once flipped, the face of the card is upside down and mirrored.
Once I've flipped the card I cannot get the animation to ever run again. In other words, I can run the animation block as many times as I want, but only the first time will animate. The subsequent times I try to animate lead to just a fade in and out between the subviews.
Also, bear in mind that I need to be able to interact with the face of the card. i.e. it has buttons on it.
If anybody has run into these issues it would be great to see your solutions. Even better would be to add a perspective transform to the animation to give it that extra bit of realism.
This turned out to be way simpler than I thought and I didn't have to use any CoreAnimation libraries to achieve the effect. Thanks to #Aaron Hayman for the clue. I used transitionWithView:duration:options:animations:completion
My implementation inside the container view:
[UIView transitionWithView:self
duration:0.2
options:UIViewAnimationOptionTransitionFlipFromBottom
animations: ^{
[self.backView removeFromSuperview];
[self addSubview:self.frontView];
}
completion:NULL];
The trick was the UIViewAnimationOptionTransitionFlipFromBottom option. Incidentally, Apple has this exact bit of code in their documentation. You can also add other animations to the block like resizing and moving.
Ok, this won't be a complete solution but I'll point out some things that might be helpful. I'm not a Core-Animation guru but I have done a few 3D rotations in my program.
First, there is no 'back' to a view. So if you rotate something by M_PI (180 degrees) you're going to be looking at that view as though from the back (which is why it's upside down/mirrored).
I'm not sure what you mean by:
Only half of the card across the x-axis is animated.
But, it it might help to consider your anchor point (the point at which the rotation occurs). It's usually in the center, but often you need it to be otherwise. Note that anchor points are expressed as a proportion (percentage / 100)...so the values are 0 - 1.0f. You only need to set it once (unless you need it to change). Here's how you access the anchor point:
layer.anchorPoint = CGPointMake(0.5f, 0.5f) //This is center
The reason the animation only ever runs once is because transforms are absolute, not cumulative. Consider that you're always starting with the identity transform and then modifying that, and it'll make sense...but basically, no animation occurs because there's nothing to animate the second time (the view is already in the state you're requesting it to be in).
If you're animating from one view to another (and you can't use [UIView transitionWithView:duration:options:animations:completion:];) you'l have to use a two-stage animation. In the first stage of the animation, for the 'card' that is being flipped to backside, you'll rotate the view-to-disappear 'up/down/whatever' to M_PI_2 (at which point it will be 'gone', or not visible, because of it's rotation). And in the second stage, you're rotate the backside-of-view-to-disappear to 0 (which should be the identity transform...aka, the view's normal state). In addition, you'll have to do the exact opposite for the 'card' that is appearing (to frontside). You can do this by implementing another [UIView animateWithDuration:...] in the completion block of the first one. I'll warn you though, doing this can get a little bit complicated. Especially since you're wanting views to have a 'backside', which will basically require animating 4 views (the view-to-disappear, the view-to-appear, backside-of-view-to-disappear, and the backside-of-view-to-appear). Finally, in the completion block of the second animation you can do some cleanup (reset view that are rotated and make their alpha 0.0f, etc...).
I know this is complicated, so you might want read some tutorial on Core-Animation.
#Aaron has some good info that you should read.
The simplest solution is to use a CATransformLayer that will allow you to place other CALayer's inside and maintain their 3D hierarchy.
For example to create a "Card" that has a front and back you could do something like this:
CATransformLayer *cardContainer = [CATransformLayer layer];
cardContainer.frame = // some frame;
CALayer *cardFront = [CALayer layer];
cardFront.frame = cardContainer.bounds;
cardFront.zPosition = 2; // Higher than the zPosition of the back of the card
cardFront.contents = (id)[UIImage imageNamed:#"cardFront"].CGImage;
[cardContainer addSublayer:cardFront];
CALayer *cardBack = [CALayer layer];
cardBack.frame = cardContainer.bounds;
cardBack.zPosition = 1;
cardBack.contents = (id)[UIImage imageNamed:#"cardBack"].CGImage; // You may need to mirror this image
[cardContainer addSublayer:cardBack];
With this you can now apply your transform to cardContainer and have a flipping card.
#Paul.s
I followed your approach with card container but when i applt the rotation animation on card container only one half of the first card rotates around itself and finally the whole view appears.Each time one side is missing in the animation
Based on Paul.s this is updated for Swift 3 and will flip a card diagonally:
func createLayers(){
transformationLayer = CATransformLayer(layer: CALayer())
transformationLayer.frame = CGRect(x: 15, y: 100, width: view.frame.width - 30, height: view.frame.width - 30)
let black = CALayer()
black.zPosition = 2
black.frame = transformationLayer.bounds
black.backgroundColor = UIColor.black.cgColor
transformationLayer.addSublayer(black)
let blue = CALayer()
blue.frame = transformationLayer.bounds
blue.zPosition = 1
blue.backgroundColor = UIColor.blue.cgColor
transformationLayer.addSublayer(blue)
let tgr = UITapGestureRecognizer(target: self, action: #selector(recTap))
view.addGestureRecognizer(tgr)
view.layer.addSublayer(transformationLayer)
}
Animate a full 360 but since the layers have different zPositions the different 'sides' of the layers will show
func recTap(){
let animation = CABasicAnimation(keyPath: "transform")
animation.delegate = self
animation.duration = 2.0
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.toValue = NSValue(caTransform3D: CATransform3DMakeRotation(CGFloat(Float.pi), 1, -1, 0))
transformationLayer.add(animation, forKey: "arbitrarykey")
}

Why is CATransform3D not working when triggered from viewWillAppear?

In viewWillAppear I want to adjust some of my views using the following transformation (Monotouch code):
CATransform3D oTransform3D = CATransform3D.Identity;
oTransform3D.m34 = 1.0f / -400;
oTransform3D = oTransform3D.Translate( 110, 0, 0);
oTransform3D = oTransform3D.Rotate( (-70f) * (float)Math.PI / 180f, 0, 1, 0);
However, this causes the view to be rendered far left of the screen.
If I put the very same code in viewDidAppear, it is working.
I have already checked that all views have valid sizes.
Did you ever figure this out? I've run into a similar situation where a 3D transform refuses to behave when applied to an immediate subview of a UIViewController's root view. A workaround for me was to wrap the subview in another container UIView.
Something along these lines:
- (void)viewDidLoad {
[super viewDidLoad];
UIView *container = [[UIView alloc] initWithFrame:CGRectMake(0,0,self.view.frame.size.width,self.view.frame.size.height)];
container.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoResizingFlexibleHeight;
[self.view addSubview:container];
self.myCrazyFlippingSpinningView = [[CrazyFlippingSpinningView alloc] init];
[container addSubview:self.myCrazyFlippingSpinningView];
}
Of course, I'd prefer to not have to do that. It's such a hack. If anyone's encountered this and has a better solution, I'd love to see it.
Thanks!
Not sure I fully understand the question but I am going to take a swag.
CATransform3D has a vanishing point that transforms refer to. The vanishing point is always the x,y center point of whatever view you place the transformed view onto. When I was struggling through the 3D transform issues and getting unexpected results, it was always solved by simply making sure I knew the size and center point of the container view.
You say your code works in ViewDidLoad...does that mean you are adding a transformed view to the base view? like: this.View.AddSubview(yourTransformedView); ??
I mean, I don't think your problem is a matter of ViewWillAppear vs. ViewDidLoad; I am going to guess that if I saw the code where you apply your transform to the views and then add your views to a base view or some other superview, I would be able to quickly tell you what is happening.