I'm using iCarousel to have a scroll of images. THe code is:
- (CATransform3D)carousel:(iCarousel *)_carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
CGFloat count = 5;
CGFloat spacing = 0.9f;
CGFloat arc = M_PI * 0.3f;
CGFloat radius = fmaxf(140.0 * spacing / 2.0f, 140.0 * spacing / 2.0f / tanf(arc/2.0f/count));
CGFloat angle = offset / count * arc;
radius = -radius;
angle = -angle;
transform = CATransform3DTranslate(transform, radius * sin(angle),radius * cos(angle) - radius, 0.0f);
return transform;
}
But when I scroll the images there is an ugly effect, the transition is not smooth and the images comes out in a jerky way, but I'd like to come out smootly. Can you help me? Thanks.
Edit: The problem is that when I scroll the images the transition is not smooth and the images comes out in front of the images on the back with a detachment from the other images. Pratically, the images comes in front of the others only when the scrolling did end and this causes a bad effect.
We had a similar issue in our last implementation of iCarousel. Here's how we fixed it:
- (CGFloat)carousel:(iCarousel *)carousel valueForOption:(iCarouselOption)option withDefault:(CGFloat)value {
if (option == iCarouselOptionSpacing) {
return value;// * 1.05f;
} else if(option == iCarouselOptionWrap) {
return NO;
} else if(option == iCarouselOptionVisibleItems) {
return 3;
}
return value;
}
Specifically, what you're probably needing is that last else if statement where we specify the number of visible items. It defaults to 1 which means that as you swipe through the new images come out in a jerky way. By specifying 3, you guarantee that the most previous item, the current item, and the next item are always loaded in memory so scrolling between them is always smooth. If this doesn't solve your problem, increase the number 3 to whatever works.
Also, don't forget to set the delegate of the iCarousel to self.
Best of luck.
Related
I want to do a demo like the game called aa. So I need to get the real-time angle about the view rotation. The following codes just can obtain final angel:
CGFloat rotationAngle = atan2f(_view.transform.b, _view.transform.a);
CGFloat degreeAngle = rotationAngle * (180 / M_PI);
In my mind, if I can know coordinates of the point insert pin during the central view is rotation, I maybe finish this demo. For example,this point can make like this:
So I want to know how to rotate the view of the current has obtained is how many degrees, or other way to realize game aa.
After some times testing, these codes can solve this problem:
- (CGFloat)transformAngleWithRotationDirection:(BOOL)clockwise {
return clockwise ? [self transformRotationAngle] : 2.0f * M_PI - [self transformRotationAngle];
}
- (CGFloat)transformRotationAngle {
CGFloat degreeAngle = - atan2f(self.presentationLayer.transform.m21, self.presentationLayer.transform.m22);
if (degreeAngle < 0.0f) {
degreeAngle += (2.0f * M_PI);
}
return degreeAngle;
}
I've created a subclassed textfield view inside my view controller, with the font size to 15, the AdjustFontSizeToWidth enabled and the minimum font size to 10.
My textfield has a placeholder text too big to fit the view. The adjust font size to width is activated and the system is reducing the size of the font to 14. I don't exactly know why 14, because the placeholder still doesn't fit (see screenshot)
Any idea why is this happening ? Did i missed something ?
I subclassed UITextfield and override the following methods (Not sure it is relevant, but this seem to be methods who may cause this bugs) :
- (CGRect)leftViewRectForBounds:(CGRect)bounds {
CGRect leftViewRect = [super leftViewRectForBounds:bounds];
if ([self shouldShowLeftView] == YES) {
CGFloat newHeight = self.frame.size.height * 0.5f;
if (self.leftImage) {
leftViewRect.size.width = newHeight;
}
leftViewRect.origin.x = kMargins;
leftViewRect.origin.y = (self.frame.size.height - leftViewRect.size.height) / 2.0f;
}
return leftViewRect;
}
- (CGRect)textRectForBounds:(CGRect)bounds {
CGRect rectForBounds = [super textRectForBounds:bounds];
if ([self shouldShowLeftView] == YES) {
rectForBounds.origin.x += kMargins;
rectForBounds.size.width -= 2.0 * kMargins;
} else {
rectForBounds.origin.x += kMargins / 2.0;
rectForBounds.size.width -= 2.0 * (kMargins / 2.0);
}
return rectForBounds;
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
CGRect rectForBounds = [super editingRectForBounds:bounds];
if ([self shouldShowLeftView] == YES) {
rectForBounds.origin.x += kMargins;
rectForBounds.size.width -= 2.0 * kMargins;
} else {
rectForBounds.origin.x += kMargins / 2.0;
rectForBounds.size.width -= 2.0 * (kMargins / 2.0);
}
return rectForBounds;
}
EDIT: Update, I tried to use this solution
and adapted the code a bit to make it work for the placeholder also. The font resizing is fine, but the UI is messed up when the text is reduced, the text offset itself to the left, behind the left image view. On the screenshot, we can see that the right margin is too big and the text too much on the left.
You aren't missing anything - this is a quirk of iOS. minimumFontSize values less than 14 seem to be completely ignored as per my answer to a similar question.
Set your original font size to 300 and the UITextField will still be 14 pt, not 10 pt.
14 is an odd magic number; undocumented, but probably used to maintain readability. If only it could be lower.
I am trying to bring out dock like magnification effect for my iPad app thru iCarousel library. With that i am able to zoom in the center item of the carousel with the following piece of code, but trying to zoom the adjacent items of the centre item with zoom level little less than the centre item.
- (CATransform3D)carousel:(iCarousel *)_carousel itemTransformForOffset:
:(CGFloat)offset baseTransform:(CATransform3D)transform
{
CGFloat MAX_SCALE = 1.95f; //max scale of center item
CGFloat MAX_SHIFT = 40.0f; //amount to shift items to keep spacing the same
CGFloat shift = fminf(1.0f, fmaxf(-1.0f, offset));
CGFloat scale = 1.0f + (1.0f - fabs(shift)) * (MAX_SCALE - 1.0f);
transform = CATransform3DTranslate(transform,
offset * _carousel.itemWidth * 1.08f + shift * MAX_SHIFT, 0.0f, 0.0f);
return CATransform3DScale(transform, scale, scale, scale);
}
Looking forward for any kind of help. thanks.
This function could be your answer:
its graph (for scaleMax = 3, xFactor = 1):
This function is used directly for calculating the scale factor from the carousel offset. In addition you need to shift the elements to left and right, so that don't overlap (as you already did). This can be done either by shifting the items by the function's integral, which works, but the gap in the center is huge this way. Or it can be calculated manually by taking a sum of all scaled items. The gap can stay constant, or it can be scaled separately.
Notice that the scale is equal to 1 in the center and descends to 1/scale_max by the edges. This is because scaling down doesn't create undesirable pixelated effects. Make your item view as you want it to appear in the center and the views on the edges will get scaled down.
This could be the usage:
-(CGFloat) scaleForX:(CGFloat)x xFactor:(CGFloat)xFactor centerScale:(CGFloat)centerScale
{
return (1+1/(sqrtf(x*x*x*x*xFactor*xFactor*xFactor*xFactor+1))*(centerScale-1.0))/centerScale;
}
- (CATransform3D)carousel:(iCarousel *)carousel itemTransformForOffset:(CGFloat)offset baseTransform:(CATransform3D)transform
{
//items in the center are scaled by this factor
const CGFloat centerScale = 4.0f;
//the larger the xFactor, the smaller the magnified area
const CGFloat xFactor = 1.5f;
//should the gap also be scaled? or keep it constant.
const BOOL scaleGap = NO;
const CGFloat spacing = [self carousel:carousel valueForOption:iCarouselOptionSpacing withDefault:1.025];
const CGFloat gap = scaleGap?0.0:spacing-1.0;
//counting x offset to keep a constant gap
CGFloat scaleOffset = 0.0;
float x = fabs(offset);
for(;x >= 0.0; x-=1.0)
{
scaleOffset+=[self scaleForX:x xFactor:xFactor centerScale:centerScale];
scaleOffset+= ((x>=1.0)?gap:x*gap);
}
scaleOffset -= [self scaleForX:offset xFactor:xFactor centerScale:centerScale]/2.0;
scaleOffset += (x+0.5)*[self scaleForX:(x+(x>-0.5?0.0:1.0)) xFactor:xFactor centerScale:centerScale];
scaleOffset *= offset<0.0?-1.0:1.0;
scaleOffset *= scaleGap?spacing:1.0;
CGFloat scale = [self scaleForX:offset xFactor:xFactor centerScale:centerScale];
transform = CATransform3DTranslate(transform, scaleOffset*carousel.itemWidth, 0.0, 0.0);
transform = CATransform3DScale(transform, scale, scale, 1.0);
return transform;
}
with result:
You can try to alter constants for different behaviors. Also changing the exponent to another even number can further widen the peak and sharpen the descent to the minimum scale.
You need to watch episode 219 from WWDC 2012 - Advanced Collection Views and Building Custom Layouts. I know it relates to collection views, but I'm sure you'll find a way to adapt that code :)
I am customizing the appearance of my popovers by subclassing UIPopoverBackgroundView. Everything is working fine, except there are some instances where there is a gap between the arrow and the content view. If I keep rotating the device to dismiss the popover then bring it back up, sometimes they line up and others they do not. I'm logging the frame sizes and they are consistently the same, so I have no idea what's going on. I'm attaching screenshots and my layoutSubviews code below.
Where you see me subtracting 4 in the case of arrow direction right this was based on trial & error of getting it to work at least some of the time. I hate coding like this because I'm not sure what this space of 4 points is and why I don't need it in all cases of arrow direction, for example.
In the images the 1st shot shows the problem. This happened on a random triggering of the popover. The next image shows it randomly working when triggered later within the same execution of the app.
Also worth mentioning, I found some sample code at http://blog.teamtreehouse.com/customizing-the-design-of-uipopovercontroller and tried this version of layoutSubviews, even though it's already very similar to what I have. Even with this code I was still seeing the same issue.
EDIT: Worth mentioning it might be obvious from my screenshots but I am not including a border for my popover:
+ (UIEdgeInsets)contentViewInsets
{
return UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
}
Possibly also worth mentioning I am not using an image file for my arrow image but rather am drawing it custom. Again, even when I comment out the drawing I'm still having the issue, so I don't think it's related, but wanted to mention it for full disclosure.
Any help greatly appreciated. Thanks.
CGSize arrowSize = CGSizeMake(kCMAPopoverBackgroundViewArrowBase, kCMAPopoverBackgroundViewArrowHeight);
// Leaving code out for drawArrowImage - even when I leave the arrowImageView as
// an empty image view and set a non-clear background color this gap is still
// sporadically showing up
self.arrowImageView.image = [self drawArrowImage:arrowSize];
float arrowXCoordinate = 0.0f;
float arrowYCoordinate = 0.0f;
CGAffineTransform arrowRotation = CGAffineTransformIdentity;
float borderMargin = 2.0f;
switch (self.arrowDirection) {
case UIPopoverArrowDirectionUp:
arrowXCoordinate = ((self.bounds.size.width / 2.0f) - (arrowSize.width / 2.0f));
arrowYCoordinate = 0.0f;
break;
case UIPopoverArrowDirectionDown:
arrowXCoordinate = ((self.bounds.size.width / 2.0f) - (arrowSize.width / 2.0f));
arrowYCoordinate = self.bounds.size.height - arrowSize.height;
arrowRotation = CGAffineTransformMakeRotation(M_PI);
break;
case UIPopoverArrowDirectionLeft:
arrowXCoordinate = 0.0f - (arrowSize.height / 2.0f) + borderMargin;
arrowYCoordinate = ((self.bounds.size.height / 2.0f) - (arrowSize.height / 2.0f));
arrowRotation = CGAffineTransformMakeRotation(-M_PI_2);
break;
case UIPopoverArrowDirectionRight:
arrowXCoordinate = self.bounds.size.width - arrowSize.height - 4.0f;
arrowYCoordinate = ((self.bounds.size.height / 2.0f) - (arrowSize.height / 2.0f));
arrowRotation = CGAffineTransformMakeRotation(M_PI_2);
break;
default:
break;
}
self.arrowImageView.frame = CGRectMake(arrowXCoordinate, arrowYCoordinate, arrowSize.width, arrowSize.height);
[self.arrowImageView setTransform:arrowRotation];
Is your arrow image square? If not, I'm guessing that the transform is affecting its frame slightly and this is generating the unexpected gap.
Possibly you are getting subpixel values in your calculations (most likely 0.5)
Try using floorf or ceilf to make sure your values are landing exactly on pixel integral values.
For example...
arrowXCoordinate = floorf(((self.bounds.size.width / 2.0f) - (arrowSize.width / 2.0f)));
See floor reference.
I've been working on a maps replacement for quite a while now. The whole thing works with a UIScrollView backed by a CATiledLayer.
To rotate my map, i rotate the layer itself. (Using CATransform3DMakeRotation) Works pretty well so far =)
But if I ever call setZoomScale method the CATransform3D that is going to be submitted to my layer is resetting the rotation to 0.
My question is, is there any way to set the zoomscale of my scrollView without loosing the applied rotation?
The same problem exists for the pinch gestures.
//Additional Infos
To rotate around the current Position, i have to edit the anchor point. Maybe this is a problem for the scaling, too.
- (void)correctLayerPosition {
CGPoint position = rootView.layer.position;
CGPoint anchorPoint = rootView.layer.anchorPoint;
CGRect bounds = rootView.bounds;
// 0.5, 0.5 is the default anchorPoint; calculate the difference
// and multiply by the bounds of the view
position.x = (0.5 * bounds.size.width) + (anchorPoint.x - 0.5) * bounds.size.width;
position.y = (0.5 * bounds.size.height) + (anchorPoint.y - 0.5) * bounds.size.height;
rootView.layer.position = position;
}
- (void)onFinishedUpdateLocation:(CLLocation *)newLocation {
if (stayOnCurrentLocation) {
[self scrollToCurrentPosition];
}
if (rotationEnabled) {
CGPoint anchorPoint = [currentConfig layerPointForLocation:newLocation];
anchorPoint.x = anchorPoint.x / rootView.bounds.size.width;
anchorPoint.y = anchorPoint.y / rootView.bounds.size.height;
rootView.layer.anchorPoint = anchorPoint;
[self correctLayerPosition];
}
}
You can implement scrollViewDidZoom: delegate method and concatenate the two transforms to achieve desired effect:
- (void) scrollViewDidZoom:(UIScrollView *) scrollView
{
CATransform3D scale = contentView.layer.transform;
CATransform3D rotation = CATransform3DMakeRotation(M_PI_4, 0, 0, 1);
contentView.layer.transform = CATransform3DConcat(rotation, scale);
}
EDIT
I've got simpler idea! How about adding another view to the hierarchy with the rotation transform attached? Here's the proposed hierarchy:
ScrollView
ContentView - the one returned by viewForZoomingInScrollView:
RotationView - the one with rotation transform
MapView - the one with all the tiles
I don't think that performance should be any concern here and it's worth trying.