Core-Plot: maintain zoomed-in range on reloadData - ios

My CPTScatterPlot allows pinching to zoom-in to see more detailed data, as well as some parameters that can be changed. Whenever the parameters are changed, I recalculate the data points, followed by reloadData. However, that again shows the whole data range, instead of keeping the zoomed-in range.
EDIT: What I have tried so far:
-(CPTPlotRange *)plotSpace:(CPTPlotSpace *)space willChangePlotRangeTo:(CPTPlotRange *)newRange forCoordinate:(CPTCoordinate)coordinate
{
CPTPlotRange *updatedRange = nil;
switch ( coordinate )
{
case CPTCoordinateX:
if ([newRange isEqualToRange: ((CPTXYPlotSpace *)space).globalXRange]) {
updatedRange = ((CPTXYPlotSpace *)space).xRange;
}
else {
updatedRange = newRange;
}
break;
case CPTCoordinateY:
case CPTCoordinateZ:
case CPTCoordinateNone:
break;
}
return updatedRange;
}
I have a hunch I need to do this in this delegate method, but I have not yet been able to figure out how.
Is there a way to maintain the zoomed-in range whenever reloadData is called?
EDIT 2: Solved by adding a dataIsLoaded flag so that I only set xRange initially, but not on every re-calculation. The delegate method above is not related to the problem.

Related

iOS: Switch UIViews using ReactiveCocoa

I am having some trouble kicking-off with ReactiveCocoa
So basically - I have 2 (potentially N) UIButtons which when tapped on should trigger a different view to be displayed on the screen.
At the moment I am achieving it in the following way:
- (IBAction)firstButtonTapped:(id)sender {
[self processViewDisplayForViewType:ViewTypeFirstType];
}
- (IBAction)secondButtonTapped:(id)sender {
[self processViewDisplayForViewType:ViewTypeSecondType];
}
-(void)processViewDisplayForViewType:(ViewType)viewType{
switch (viewType) {
case ViewTypeFirstType:
//process showing a view based on the given type
break;
case ViewTypeSecondType:
//process showing a view based on the given type
break;
default:
break;
}
self.currentViewType = viewType;
}
Now, if I have other display types I would need to add aditional statements in the switch block... so I hope I can solve this using ReactiveCocoa and FRP. Anybody can point me in the right direction on solving this ?

CCLabelTTF changing string causes EXC_BAD_ACCESS

First off, as a disclaimer, I am new to objective-c, xcode, and cocos2d. I have found a way in my app to refresh a screen conveniently, but I don't know if it is bad practice. Here's what I am doing. I have a class called Player with a variable NSString *name. I am displaying this and several other variables on the screen using this code in a function:
label = [CCLabelTTF labelWithString:string fontName:GLOBAL_FONT fontSize:font_size ];
label.color = color_back;
label.position = ccp(x+1, y-1);
[self addChild:label];
When a button is pressed, I am modifying player->name along with several other variables. Because several variable are changing (on this screen and eventually others) when the button is pressed I also set a flag to indicate that the screen needs to be refreshed. I then check this code with a scheduler:
if(panelPrev != currentPanel || refreshScreen) //do we need to initialize the panel?
{
[self removeAllChildrenWithCleanup:true]; //clear all objects from display
//Decide which objects to display
switch (G_display_panel) {
case P_Main:
[Display_Main init_Panel:self];
break;
case P_NewGame:
[Display_New init_Panel:self];
break;
default:
break;
}
refreshScreen = false;
}
My first question: Is this an acceptable way to display things to the screen and refresh them? It seemed much more convenient than updating every variable that is being displayed. The buttons are not being pressed very often so I am not concerned about performance.
Second: If it is ok to do it this way, why is it that when I press a button and change the value of player->name I am getting this: "Thread 1: EXC_BAD_ACCESS (code=1, address=...)"? If I step through with the debugger the value gets assigned to player->name correctly and the screen refresh works. But if I just let it run it gets the EXC_BAD_ACCESS when I try to access player->name and the data looks corrupted (e.g. (NSString *) name = 0x15927f80 when I am expecting (NSString *) name = #"Bob").
Some additional details.
I am not setting refreshScreen to 'true' until after changing the value of player->name
To prevent refreshing before the value was truly changed, I set a delay on the refresh. After the button was pressed I would modify player->name and wait about 10 seconds but I would still see the same problem.
Any ideas? Thanks.
try this:
[self addChild:label];
I figured out the problem. It was a memory management issue. I added a getter and setter for the variable using the example specified here: developer.apple.com/library/mac/documentation/Cocoa/Conceptual/… –

ios - I need to get the referencing outlet, or some other identifier from the sender (UITextView)

Warning: I am new to ios development but not coding in general.
I am trying to do some validation on a UITextView, several actually. All have slightly different requirements for length. I have created a custom delegate to handle this but I am running into an issue with figuring out how to make it unique to the textview sending it.
I have several fields in several different nibs that will make use of this delegate. Most of the nibs will have 3 textviews labeled something like: summary, detail and special instructions. each of these has a different max length 50, 100, 130 respectively.
TL:DR; How can I get a unique id for each sender so I can do a switch on them?
EDIT: I also would like to update a label, hidden until needed, to count down the available chars left. How could I make sure I am accessing the correct controller and label?
In your delegate protocol, define a way to send the UITextView instance along with anything else, something like:
#protocol UITextFieldValidationDelegate
- (BOOL) textField:(UITextField)tf textForValidation:(NSString *)newText;
#end
So that when the UITextFields call this, they pass themselves in for inspection by the delegate:
BOOL valid = [self.delegate textField:self textChanged:newText];
In the delegate, you'll implement this as:
- (BOOL) textField:(UITextField)tf textForValidation:(NSString *)newText
{
//compare to your IBOutlets
if (tf == self.tfPhoneNumber)
{
//Do phone number validation
}
else if (tf == self.tfEmail
{
//Do email validation
}
if (valid)
{
return YES;
}
else
{
return NO;
}
}
Add a tag to each textView, and check the tag in the delegate methods.
//In your viewcontroller setup
someTextView.tag = 0;
someOtherTextView.tag = 1;
someThirdTextView.tag = 2;
//In your delegate methods
if(textView.tag == 0)
//do something
else if(textView.tag == 1)
//do something else
else if(textView.tag == 2)
//do a third thing

How to implement setNeedsReload / setNeedsRefresh

I've been wondering about extending UIView with a sort of setNeedsRefresh method of my own - a method that behaves like setNeedsLayout and setNeedsDisplay but with the purpose of refreshing/reloading visible data, hiding views, etc. My goals would be to:
Be able to detach methods (particularly setters) from having to instantly refresh views.
Be able to flag a view for refresh rather than refreshing it manually (in order to optimize code execution and have a cleaner code architecture).
I was hoping to be able to extend UIView in such a way that you can call setNeedsRefresh on any view, and have it execute refreshView or some similar method before redrawing happens.
PS: is the best way to handle this through setNeedsLayout + a refresh flag?
Update #1: The following is an example of what I want to be able to do:
-(void)setAvailableForPurchase:(BOOL)availableForPurchase
{
if (_availableForPurchase != availableForPurchase)
{
_availableForPurchase = availableForPurchase;
[self setNeedsRefresh];
}
}
And then:
-(void)refreshView
{
if (self.isAvailableForPurchase)
{
self.someView.image = someImage;
self.someButton.enabled = yes;
// more code, changes several objects
}
else
{
// some other code...
}
}
This example is a very simple one on purpose to better express my requirement - the reasons why I need/want to do this are listed in my initial question.

NSFetchedResultsChangeUpdate crashes when called on a searched tableview

So I nearly have this thing figured out, but I am stumbling over the NSFetchedResultsChangeUpdate when I update my managedObjectContext from a detail view that was entered after searching the table.
I have a tableview generated from a core data set. I can enter a detail view from this table and make changes without any issue. I can also search the table and make changes MOST of the time without any issues. However, on certain objects, I get an "Exception was caught during Core Data change processing".
I tracked this down to the NSFetchedResultsChangeUpdate. I'm using the following code:
case NSFetchedResultsChangeUpdate:
if (searchTermForSegue)
{
NSLog(#"index info:%#.....",theIndexPath);
NSLog(#"crashing at the next line");
[self fetchedResultsController:self.searchFetchedResultsController configureCell:[tableView cellForRowAtIndexPath:theIndexPath] atIndexPath:theIndexPath];
break;
} else {
[self fetchedResultsController:controller configureCell:[tableView cellForRowAtIndexPath:theIndexPath] atIndexPath:theIndexPath]; }
break;
When the table is not being searched, it runs the else method and that works 100% of the time. When the table is being searched, it runs the if (searchTermForSegue) and that works most of the time, but not always. I logged theIndexPath and discovered the following:
When it works, theIndexPath is correctly reporting the objects indexPat, when it fails, the wrong theIndexPath has been called. For example, if I do a search that narrows the tableView to 3 sections, 2 items in first, 1 in second, 1 in third, I get the following nslog:
On first object: index info:<NSIndexPath 0xb0634d0> 2 indexes [0, 0].....
on second object: index info:<NSIndexPath 0xb063e70> 2 indexes [0, 1].....
on third object: index info:<NSIndexPath 0xb042880> 2 indexes [1, 0].....
but on the last object: index info:<NSIndexPath 0x9665790> 2 indexes [2, 17].....
it should be calling [2, 0]
Note that I am simply updating these objects, not deleting them or adding new ones.
Any thoughts would be appreciated!
Okay, I have figured out a hack that seems to fix the issue, but it doesn't seem like the best way to solve the problem! Any thoughts?
I discovered when NSFetchedResultsChangeUpdate was called from a base table, it was only called once, but when it was called from a searched/filtered table it was called twice (seemingly 100% of the time - I suppose this is to refresh both the searched table and non-searched table?). In addition, the first call always seemed to have the incorrect indexPath while the second call had the correct information. The first call would crash the app if the incorrect indexPath was out of bounds.
My "fix" was to ignore the first call if NSFetchedResultsChangeUpdate from a searched table by doing the following
adding to the interface of the tableview.h file:
int changeCall;
then, in the tableview.m file, in loadView:
changeCall = 0;
Then I updated the NSFetchedResultsChangeUpdate to this:
case NSFetchedResultsChangeUpdate:
if (searchTermForSegue)
{
if (changeCall == 0) {
changeCall++;
break;
} else if (changeCall == 1){
NSLog(#"index infocalledtwice:%#.....",theIndexPath);
[self fetchedResultsController:self.searchFetchedResultsController configureCell:[tableView cellForRowAtIndexPath:theIndexPath] atIndexPath:theIndexPath];
changeCall = 0;
break;
} else { break; }
} else {
[self fetchedResultsController:controller configureCell:[tableView cellForRowAtIndexPath:theIndexPath] atIndexPath:theIndexPath];
}
break;
Essentially, using the changeCall int to skip the first call in a searched table update but use the second call to actually update the content. This CAN'T be the best way to fix this problem, but the bandaid seems to work. I would certainly appreciate if someone has a better answer.
In addition, by skipping the first NSFetchedResultsChangeUpdate, the non-searched table is not updated to reflect the new values. I've fixed this by adding a call to reload the table in the searchDisplayControllerWillEndSearch method:
- (void)searchDisplayControllerWillEndSearch:(UISearchDisplayController *)controller
{
[self setSearchTermForSegue:nil];
[self.tableView reloadData];
}
Thanks!

Resources