Core Plot range change performance - ios

I need to resize my plot using pinch recognizers, its allowed to resize "width" and "height" separately in small areas at left and bottom.
Also my plot is inside of UIScrollView to scroll it horizontally
This is my Y-axis pinch code:
- (void)yAxisPinchPerformed:(UIPinchGestureRecognizer *)pinchRecognizer
{
CGFloat recognizerScale = pinchRecognizer.scale;
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)_graph.defaultPlotSpace;
switch (pinchRecognizer.state) {
case UIGestureRecognizerStateBegan:
{
_savedRange = plotSpace.yRange;
}
break;
case UIGestureRecognizerStateChanged:
{
CGFloat scaledRange = recognizerScale * _savedRange;
[plotSpace setYRange:scaledRange];
}
break;
default:
break;
}
}
works good, but really slow on devices, probably because it redraws the whole plot (it's wide and inside UIScrollView), not only visible area;
my plot contains maximum 100 bars with gradient fill. I tried to turn off filling but it helped just a little;
now i have these improvements:
- (void)yAxisPinchPerformed:(UIPinchGestureRecognizer *)pinchRecognizer
{
CGFloat recognizerScale = pinchRecognizer.scale;
switch (pinchRecognizer.state) {
case UIGestureRecognizerStateBegan:
{
// UIImageView *_imageView;
[_imageView setImage:_graph.imageOfLayer];
[_imageView setHidden:NO];
// CGRect initialFrame is frame of graph container
[_imageView setFrame:initialFrame];
_savedHeigth = _imageView.frame.size.height;
}
break;
case UIGestureRecognizerStateChanged:
{
CGFloat scaledHeight = recognizerScale * _savedHeight;
CGRect imageFrame = _imageView.frame;
imageFrame.size.height = scaledHeight;
[_imageView setFrame:imageFrame];
}
break;
case UIGestureRecognizerStateEnded:
CGFloat scaledRange = calculateRangeFromHeights(...);
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)_graph.defaultPlotSpace;
[plotSpace setYRange:scaledRange];
[_imageView setHidden:YES];
break;
default:
break;
}
}
I get image of plot layer and perform scaling of UIImageView, it's super faster. But when i decrease Y-scale i have only part of graph, it's cutted at the top.
Is it possible to scale Core Plot graph smoothly and real-time?

Core Plot has no way to know how much of the graph is visible inside your scroll view. I would remove the scroll view and rely on the built-in Core Plot scrolling. If that's not feasible, you could determine which bars are visible and only show the visible bars. Modify your datasource to return data only for the visible bars and reload the data whenever the set of visible bars changes.

Related

Pangesture Fails and then stuck on view

My app consists of a deck of cards. Each card has a scrollview that allows you to look at images associated with that card. You can drag to look at another card in the deck via pan gesture. Unfortunately, if you don't do a clean drag the first time (like if you drag half way and then lift your finger off), the app gets stuck on that card. You can scroll up and down, but you won't be able to drag. When you try, you just move backgroundScrollView around within the superview(so swiping right means you see the grey, right margins between scrollview and superview).
This is the method that UIPanGestureRecognizer calls:
-(void)beingDragged:(UIPanGestureRecognizer *)gestureRecognizer {
NSLog(#"I am being dragged");
xFromCenter = [gestureRecognizer translationInView:self].x; //%%% positive for right swipe, negative for left
yFromCenter = [gestureRecognizer translationInView:self].y; //%%% positive for up, negative for down
switch (gestureRecognizer.state) {
//%%% just started swiping
case UIGestureRecognizerStateBegan:{
self.backgroundScrollView.scrollEnabled = NO;
self.originalPoint = self.center;
_backgroundScrollView.autoresizingMask = UIViewAutoresizingNone;
_backgroundScrollView.translatesAutoresizingMaskIntoConstraints = YES;
likeBadge.autoresizingMask = UIViewAutoresizingNone;
likeBadge.translatesAutoresizingMaskIntoConstraints = YES;
passBadge.autoresizingMask = UIViewAutoresizingNone;
passBadge.translatesAutoresizingMaskIntoConstraints = YES;
reviewButton.autoresizingMask = UIViewAutoresizingNone;
reviewButton.translatesAutoresizingMaskIntoConstraints = YES;
break;
};
//%%% in the middle of a swipe
case UIGestureRecognizerStateChanged:{
//%%% dictates rotation (see ROTATION_MAX and ROTATION_STRENGTH for details)
CGFloat rotationStrength = MIN(xFromCenter / ROTATION_STRENGTH, ROTATION_MAX);
//%%% degree change in radians
CGFloat rotationAngel = (CGFloat) (ROTATION_ANGLE * rotationStrength);
//%%% amount the height changes when you move the card up to a certain point
CGFloat scale = MAX(1 - fabsf(rotationStrength) / SCALE_STRENGTH, SCALE_MAX);
//%%% move the object's center by center + gesture coordinate
self.center = CGPointMake(self.originalPoint.x + xFromCenter, self.originalPoint.y + yFromCenter);
//%%% rotate by certain amount
CGAffineTransform transform = CGAffineTransformMakeRotation(rotationAngel);
//%%% scale by certain amount
CGAffineTransform scaleTransform = CGAffineTransformScale(transform, scale, scale);
//%%% apply transformations
self.transform = scaleTransform;
[self updateOverlay:xFromCenter:yFromCenter];
break;
};
//%%% let go of the card
case UIGestureRecognizerStateEnded: {
self.backgroundScrollView.scrollEnabled = YES;
[self afterSwipeAction];
break;
};
case UIGestureRecognizerStatePossible:{
self.backgroundScrollView.scrollEnabled = YES;
};
case UIGestureRecognizerStateCancelled:{
self.backgroundScrollView.scrollEnabled = YES;
};
case UIGestureRecognizerStateFailed:{
self.backgroundScrollView.scrollEnabled = YES;
};
}
}
My solution has been to set
_backgroundScrollView.translatesAutoresizingMaskIntoConstraints = NO;
whereas before it was set to YES. This does mean that scrollView now leaks out of the view when you swipe, but that's for another post...

Subviews moving out of place when parent view is rotated

I'm following this guide here: http://guti.in/articles/creating-tinder-like-animations/ in order to recreate a Tinder-like swiping card effect.
Long story short, I have a UIView with a PanGestureRecognizer set to do the following:
- (void)dragged:(UIPanGestureRecognizer *)gestureRecognizer
{
CGFloat xDistance = [gestureRecognizer translationInView:self].x;
CGFloat yDistance = [gestureRecognizer translationInView:self].y;
switch (gestureRecognizer.state) {
case UIGestureRecognizerStateBegan:{
self.originalPoint = self.center;
break;
};
case UIGestureRecognizerStateChanged:{
CGFloat rotationStrength = MIN(xDistance / 320, 1);
CGFloat rotationAngel = (CGFloat) (2*M_PI * rotationStrength / 16);
CGFloat scaleStrength = 1 - fabsf(rotationStrength) / 4;
CGFloat scale = MAX(scaleStrength, 0.93);
self.center = CGPointMake(self.originalPoint.x + xDistance, self.originalPoint.y + yDistance);
CGAffineTransform transform = CGAffineTransformMakeRotation(rotationAngel);
CGAffineTransform scaleTransform = CGAffineTransformScale(transform, scale, scale);
self.transform = scaleTransform;
break;
};
case UIGestureRecognizerStateEnded: {
[self resetViewPositionAndTransformations];
break;
};
case UIGestureRecognizerStatePossible:break;
case UIGestureRecognizerStateCancelled:break;
case UIGestureRecognizerStateFailed:break;
}
}
- (void)resetViewPositionAndTransformations
{
[UIView animateWithDuration:0.2
animations:^{
self.center = self.originalPoint;
self.transform = CGAffineTransformMakeRotation(0);
}];
}
However, unlike the view in the example, mine has multiple different subviews, and I'm finding that when I apply this code, I get this erroneous result:
Before Swipe:
During Swipe:
As you can see, it seems as if the subviews are "sliding" out of place, so to speak. What's worse, when the swipe is over, the subviews are left looking slightly out of place, even though the parent view is in the right place. If you're curious, my constraints look like:
If anyone could help me get the subviews to move correctly with the superview, I would really appreciate it. One thing I've found sort-of remedies the last problem is to store the original centers of each of the subviews and restore them to that at the end, but I'm sure there must be a better way.
EDIT: I'm still haven't come up with a fix, but I'm now 99.9% sure it's to do with the constraints of the subviews.

iOS gradient animating with uiscrollview paging

I have an horizontal scrollview with 3 pages.
I would like to have one background color different for each page and draw gradient between the 3 colors.
Page 1 with green, page 2 with blue and page 3 with red.
How can I do that?
I was thinking about ScrollViewDidScroll delegate method:
- (void)scrollViewDidScroll:(UIScrollView *)sender
{
CGFloat pageWidth = self.statisticScrollView.frame.size.width;
float fractionalPage = self.statisticScrollView.contentOffset.x / pageWidth;
NSLog(#"fractional Page: %f", fractionalPage);
NSInteger lowerNumber = floor(fractionalPage);
NSLog(#"lower Page: %i", lowerNumber);
NSInteger upperNumber = lowerNumber + 1;
NSLog(#"upper Page: %i", upperNumber);
if (self.lastContentOffset > sender.contentOffset.x){
//RIGHT --->
if (lowerNumber == 0) {
//gradient green to blue
}else if (lowerNumber == 1){
//gradient blue to red
}else if (lowerNumber == 2){
//end pages
}
}else if (self.lastContentOffset < sender.contentOffset.x){
//LEFT <----
if (lowerNumber == 0) {
//gradient blue to green
}else if (lowerNumber == 1){
//gradient red to blue
}else if (lowerNumber == 2){
//end pages
}
}
}
Can I use fractionalPage to set the percentage of the gradient?
I read about startPoint and endPoint properties of the CAGradientLayer, but It seems not working correctly.
Any suggestions will be appreciated!
Thank you in advance
The startPoint and endPoint properties are measured from 0 to 1 and NOT in the coordinate system of the view which may be an issue you are encountering. So to run the gradient from side to side you need to declare it like this.
gradlayer.startPoint = CGPointMake(0, 0.5);
gradlayer.endPoint = CGPointMake(1, 0.5);
A simple way of achieving what you want to do is to add a sublayer to the scroll view with the gradient pre-configured.
CGFloat width = CGRectGetWidth(self.view.frame);
CGFloat height = CGRectGetHeight(self.view.frame);
self.scroller.contentSize = CGSizeMake(3*width, height);
self.gradlayer = [CAGradientLayer layer];
self.gradlayer.frame = CGRectMake(0, 0, width*3, height);
[self.scroller.layer addSublayer:self.gradlayer];
self.gradlayer.colors = #[((id)[UIColor redColor].CGColor),((id)[UIColor greenColor].CGColor),((id)[UIColor blueColor].CGColor)];
self.gradlayer.startPoint = CGPointMake(0, 0.5);
self.gradlayer.endPoint = CGPointMake(1, 0.5);
However this may cause memory issues if your content size gets much larger.
I had a look on the Sim and it didn't seem grow with more pages. I went to 10 and it seemed to stay about the same as 3 pages but it is a consideration so you may have to go back to your original theory of adjusting the color array depending on the horizontal offset.
I think the scrollviews internal paging handles its sublayers effectively to avoid this.
Sweet color scheme though. Jerry Garcia would be proud.

how to move one direction in UIScrollView when scrolling

I'm new in objective-c. I create UIScrollView object and add in my view with this code:
height = self.view.frame.size.height;
width = self.view.frame.size.width;
scrollbar = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
scrollbar.delegate = self;
scrollbar.backgroundColor = [UIColor whiteColor];
scrollbar.maximumZoomScale = 1.0;
scrollbar.minimumZoomScale = 1.0;
scrollbar.clipsToBounds = YES;
scrollbar.showsHorizontalScrollIndicator = YES;
scrollbar.pagingEnabled = YES;
[scrollbar setContentSize:CGSizeMake(width*4, height*4)];
[self.view addSubview:scrollbar];
for (int i = 1; i <= 4; i++) {
first = [[FirstViewController alloc]initWithNibName:#"FirstViewController" bundle:nil];
first.view.frame = CGRectMake((i-1)*width, 0, width, height*4);
[scrollbar addSubview:first.view];
switch (i) {
ase 1:
first.view.backgroundColor = [UIColor blueColor];
break;
case 2:
first.view.backgroundColor = [UIColor redColor];
break;
case 3:
first.view.backgroundColor = [UIColor greenColor];
break;
case 4:
first.view.backgroundColor = [UIColor grayColor];
break;
default:
break;
}
}
in my code I add 4 view in my ScrollView with different color now I want when scrolling on my ScrollView detect dx & dy (dx: driving distance on Axis.x & dy:driving distance on Axis.y) and check these two variable and when :
Notic: I want when any one touch on ScrollView and moving touch on Axis (x or y) or touch on Both Axis (x and y) check this :
if (dx > dy) disable horizontal scroll and moving in vertical side!!!
else moving in horizontal side and disable vertical scroll!!!
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGRect visibleRect = CGRectMake(scrollView.contentOffset.x, scrollView.contentOffset.y, scrollView.contentOffset.x + scrollView.bounds.size.width, scrollView.contentOffset.y + scrollView.bounds.size.height);
NSLog(#"%f,%f",visibleRect.origin.x,visibleRect.origin.y);
/*NSLog(#"x : %f",scrollView.contentOffset.x);
NSLog(#"y : %f",scrollView.contentOffset.y);*/
if (fabsf(scrollView.contentOffset.x) > fabsf(scrollView.contentOffset.y)) {
NSLog(#"Vertical Side");
}
else {
NSLog(#"Horizontal Side");
}
}
please guide me guys. I can't disable one side and move another side!!! thanks
If i understood you right, you want to disable scrolling in the direction that has been dragged less by the user. If so, UIScrollView already offers an option to do so:
scrollView.directionalLockEnabled = YES;
When this option is set to true UIScrollView will handle the correct direction lock by itself.
For more information look at the documentation: link
You can achieve the result you want adding some code to your delegate. This is my ViewController.m file. -viewDidLoad, #import statements and the other methods are omitted.
#interface ViewController () {
CGPoint initialOffset;
NSInteger direction; // 0 undefined, 1 horizontal, 2 vertical
}
#end
#implementation ViewController
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
// retrieve current offset
CGPoint currentOffset = scrollView.contentOffset;
// do we know which is the predominant direction?
if (direction == 0) {
// no.
CGFloat dx = currentOffset.x - initialOffset.x;
CGFloat dy = currentOffset.y - initialOffset.y;
// we need to decide in which direction we are moving
if (fabs(dx) >= fabs(dy)) {
direction = 1; // horizontal
} else if (fabs(dy) > fabs(dx)) {
direction = 2;
}
}
// ok now we have the direction. update the offset if necessary
if (direction == 1 && currentOffset.y != initialOffset.y) {
// remove y offset
currentOffset.y = initialOffset.y;
// update
[scrollView setContentOffset:currentOffset];
} else if (direction == 2 && currentOffset.x != initialOffset.x) {
currentOffset.x = initialOffset.x;
[scrollView setContentOffset:currentOffset];
}
}
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
// store the current offset
initialOffset = scrollView.contentOffset;
// reset flag
direction = 0; // AKA undefined
}
#end
When you start dragging, the delegate will reset the flag direction to "unknown" state, and store the current content offset. After every dragging move, your -scrollViewDidScroll: will be called. There, you decide which is the predominant direction (if this hasn't been done yet) and correct the current scrolling offset accordingly, by removing the x (or y) offset.
I tested this with the same settings you provided, only I used a UIImageView inside UIScrollView and I set up everything via InterfaceBuilder, but it should work fine. Theoretically, with this method you could replace directionLock, but remember that -scrollViewDidScroll is called many times during an action, and every time it rewrites the content offset (if the scrolling is happening in both directions). So if you leave directionLock enabled, you save many of the calls to setContentOffset: that the delegate performs.

Drawing on a zoomable view

I'm working on a small drawing application, which has a basic requirement of supporting zoom-in/out. I have two main issues:
Drawing doesn't appear crisp and clear, when view is zoomed/transformed. Is there a better approach, or is there a way to improve the drawing when the view is zoomed?
The drawing performance is slow, when drawing on 1200 x 1200 pts canvas (on iPhone). Any chance I can improve it for large canvas sizes?
Zooming Code:
- (void)scale:(UIPinchGestureRecognizer *)gestureRecognizer {
[self adjustAnchorPointForGestureRecognizer:gestureRecognizer];
UIView *canvas = [gestureRecognizer view];
if ([gestureRecognizer state] == UIGestureRecognizerStateBegan ||
[gestureRecognizer state] == UIGestureRecognizerStateChanged) {
// Calculate the drawing view's size
CGSize drawingViewSize = ...;
// Calculate the minimum allowed tranform size
// Developer's Note: I actually wanted to use the size 1/4th of the view
// but self.view.frame.size doesn't return the correct (actual) width and height
// It returns these values inverted i.e. width as height, and vice verse.
// The reason is that the view appears to be transformed (90 degrees).
// Since there's no work-around this, so for now, I'm just using fixed values.
CGSize minTranformSize = CGSizeMake(100.0f, 100.0f);
if ((minTranformSize.width > drawingViewSize.width) && (minTranformSize.height > drawingViewSize.height)) {
minTranformSize = drawingViewSize;
}
// Transform the view, provided
// 1. It won't scale more than the original size of the background image
// 2. It won't scale less than the minimum possible transform
CGSize transformedSize = CGSizeMake(canvas.frame.size.width * [gestureRecognizer scale],
canvas.frame.size.height * [gestureRecognizer scale]);
if ((transformedSize.width <= drawingViewSize.width) && (transformedSize.height <= drawingViewSize.height) &&
(transformedSize.width >= minTranformSize.width) && (transformedSize.height >= minTranformSize.height)) {
canvas.transform = CGAffineTransformScale([canvas transform],
[gestureRecognizer scale],
[gestureRecognizer scale]);
}
[gestureRecognizer setScale:1.0];
} else if ([gestureRecognizer state] == UIGestureRecognizerStateEnded) {
// Recenter the container view, if required (piece is smaller than the view and it's not aligned)
CGSize viewSize = self.view.bounds.size;
if ((canvas.frame.size.width < viewSize.width) ||
(canvas.frame.size.height < viewSize.height)) {
canvas.center = CGPointMake(viewSize.width/2, viewSize.height/2);
}
// Adjust the x/y coordinates, if required (piece is larger than the view and it's moving outwards from the view)
if (((canvas.frame.origin.x > 0) || (canvas.frame.origin.y > 0)) &&
((canvas.frame.size.width >= viewSize.width) && (canvas.frame.size.height >= viewSize.height))) {
canvas.frame = CGRectMake(0.0,
0.0,
canvas.frame.size.width,
canvas.frame.size.height);
}
canvas.frame = CGRectIntegral(canvas.frame);
}
}
Drawing Code
- (void)draw {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSaveGState(context);
if (self.fillColor) {
[self.fillColor setFill];
[self.path fill];
}
if ([self.strokeColor isEqual:[UIColor clearColor]]) {
[self.path strokeWithBlendMode:kCGBlendModeClear alpha:1.0];
} else if (self.strokeColor) {
[self.strokeColor setStroke];
[self.path stroke];
}
CGContextRestoreGState(context);
}
This is a pretty complicated problem, that I have struggled a lot with.
I ended up converting the drawings to vector.
draw all lines in one layer, draw all fills in another.
Convert the line drawings to Vector, using potrace (http://potrace.sourceforge.net/)
draw the vector using SVGKit
(https://github.com/SVGKit/SVGKit) and hide the layer drawn in 1)
It is working pretty well and fairly fast, but it requires a lot of work. We have an app in our company that applies this technique:
https://itunes.apple.com/us/app/ideal-paint-hd-mormor/id569450492?mt=8.
If your only problem is performance, try taking a look at CATiledLayer. (also used in app mentioned above). It will increase performance tremendously (You can find a pretty good tutorial here http://www.cimgf.com/2011/03/01/subduing-catiledlayer/).
Good luck! :)
First of all You are Transforming View this is not better way for zooming .Transforming may use only for increase or decrease size of UIVew For zooming you can d this.
1) get get pic of screen by using this code
UIGraphicsBeginImageContext(self.drawingView.bounds.size);
CGContextRef context = UIGraphicsGetCurrentContext();
//[self.view.layer renderInContext:context];
[self.layerContainerView.layer renderInContext:context];
UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();
[scaleLabel setHidden:FALSE];
return screenShot;
2) then put it on some UIImageView and then perform Zomming on this Image
scrollView.userInteractionEnabled=TRUE;
self.scrollView.minimumZoomScale = 1.000;
self.scrollView.maximumZoomScale = 30.0f;
[self centerScrollViewContents];
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
3) then again Set zoomed image on uiView's background
this works perfect for me hope this will also works for you

Resources