I'm in the process of writing a fairly simple Math's puzzle game for one of my children.
I used the standard XCode SpriteKit Game template which creates an SKView with an SKScene and ViewController.m and MyScene.m files.
I then added a simple UIView to act as a container for a NumberPad 0-9 with UIButtons.
I'm tempted to target MyScene.m as the target for the IBActions as it will use the changes in state from the button presses.
However I'm wondering which class is a better target for the IBActions the ViewController or MyScene. In particular are there any performance implications to either choice.
My main concern is some articles I've seen about issues people have encountered when mixing SpriteKit and UIKit.
For your purposes the performance implications are completely negligible. However from a code point of view I would probably target the viewcontroller, in case you want to swap out the scene but reuse your numberpad. It sounds like a likely development in the future for a math type game.
I took Theis' advice and so my view controller, which creates the scene, contains the following code.
ViewController.m
- (IBAction)numberPressed:(id)sender {
UIButton *pressedButton = (UIButton*) sender;
NSString *button = pressedButton.currentTitle;
[scene.keysPressed addObject:button];
}
- (IBAction)clearPressed:(id)sender {
UIButton *pressedButton = (UIButton*) sender;
NSString *button = pressedButton.currentTitle;
[scene.keysPressed addObject:button];
}
And then in my scene code I have declared the keysPressed property, being somewhat paranoid I've made it atomic in case the view controller and scene ever run in different threads.
MyScene.h
#property (strong, atomic) NSMutableArray *keysPressed;
And then in the scenes update method I just check the mutable array I'm using as a stack to see anything has been added and get its value and delete it.
MyScene.m
-(void)update:(CFTimeInterval)currentTime {
...
NSString *keyNum = nil;
if([_keysPressed count] > 0) {
keyNum = [_keysPressed firstObject];
[_keysPressed removeObjectAtIndex:0];
}
So far everything is behaving itself.
Related
I am new to iOS development and I am currently reading the book : iOS Programming (Objective C) by Big Nerd Ranch.
I am confused as in where to initialize subviews such as UIButtons, UIImageView while creating views programtically:
Should the intialization be done in the Main UIView i.e in the
initWithFrame method and maintain a additional weak reference to the subview in the UIView.
or
should I do it in the UIViewControllers loadView method and maintain a weak reference to the subview in the uiviewcontroller (Same approach used while creating UIVew using the interface builder).
I have seen both the approaches being used in various stackoverflow posts but no post that explains which approach is the right one.
you can initialize as per your app's requirement. If any view or button or anything is part of initial setup of your app then you should initialize it in viewDidload.
Now, for example there is requirement like user press button and then new view will be created then you can initialize view in button's click method etc.
So, it's depends on your requirement.
Static views which will live from start to and of app should be initialize in viewdidload, because this is the first method getting called of viewcontroller.
hope this will help :)
It dependes on which architecture you are using. Apple raises the flag of Model-View-Controller, but in fact, UIViewControllers are the View.
For Example:
Let's say that you have a pretty LoginViewController. When you instantiate it, you will be doing something like
LoginViewController *loginVC = [[LoginViewController alloc] init];
At this point, no view is loaded. Your ViewController has just executed the init method, nothing else. When the system calls
loginVC.view
the first method to be executed will be
- (void)loadView;
there you should do exactly that, load your view. So, the approach i like is to have an additional LoginView.
- (void)loadView
{
// you should have a property #property (nonatomic, strong) LoginView *loginView;
self.loginView = [[LoginView alloc] init];
self.view = self.loginView;
}
and in the LoginView init method, you should put your code to build up the view.
However, you could eliminate LoginView, and instantiate all your subviews like this:
- (void)loadView
{
self.view = [[UIView alloc] init];
UIButton *button = [[UIButton alloc] initWithTargetBlaBlaBla...];
[self.view addSubview:button];
// add more fancy subviews
}
In my experience, the first approach is much cleaner than the second one. It also makes version control a lot easier (try to merge a xib, I dare you). I always use MyView.m to build the view (a.k.a setup constriants, style) and use MyViewController.m things like animations, lifeCycle. I like to think that MyView.m is the programatic xib, so anything that you can do with xibs, you should me able to do it inside your view.
Hope it helps!!
I am completely stumped and have been researching for days. Probably something really simple that I am missing.
I have a ViewController which contains a custom UIView called GameView, and a UIView called buttonBox which contains a "next level" button. What I am trying to achieve is when the level is completed in GameView, it fires a function in my ViewController which shows the buttonBox so the user can click the "next level" button. It simply will not work.
I have attempted this in 3 ways, neither have worked:
Creating an IBOutlet in the ViewController, connecting it to the hidden UIView (and it was definitely connected) and calling setHidden:NO.
Calling the [self.view viewWithTag:xxx] and then calling setHidden:NO.
Using hidden=NO instead of setHidden:NO.
Relevant code for ViewController as follows:
#interface PlayViewController : UIViewController
#property GameView *gv;
#property (strong, nonatomic) IBOutlet UIView *buttonBox;
-(void) showButtonBox;
#end
#implementation PlayViewController
#synthesize buttonBox;
...
- (IBAction)showButtonBox {
UIView *uiv = (UIView*) [self.view viewWithTag:999];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Showing box function");
NSLog(#"%#", uiv);
uiv.hidden = NO;
});
}
#end
And my custom view:
#implementation GameView
...
dispatch_async(bgQueue, ^{
_loopRunning = true;
//NSLog(#"Calling main loop...");
while ([self loopRunning])
{
...
PlayViewController * pvc = [[PlayViewController alloc]init];
[pvc showButtonBox];
...
}
#end
The thing is, the variable uiv is returning null in NSLog, which is obviously why hidden is not working, but I have no idea why. It also didn't work when I was using IBOutlet.
Also, current output from NSLog is as follows:
2015-11-24 00:18:38.612 ib[12579:1264539] Showing box function
2015-11-24 00:18:38.612 ib[12579:1264539] (null)
Thanks in advance.
Correct Answer:
The problem was that I was using StoryBuilder to build my UI, but by using the alloc init method was creating a new view controller (which is never shown) instead of correctly referencing the view controller which was being displayed. This is achieved by passing the view controller being displayed to the view in the viewDidLoad function, see below:
#implementation PlayViewController
#synthesize buttonBox;
#synthesize gv;
- (void)viewDidLoad
{
[super viewDidLoad];
gv = [self.view viewWithTag:777];
[gv setPlayViewController:self];
}
...
Man, it's simple. Let's take a look at:
#implementation GameView
...
dispatch_async(bgQueue, ^{
_loopRunning = true;
//NSLog(#"Calling main loop...");
while ([self loopRunning])
{
...
PlayViewController * pvc = [[PlayViewController alloc]init];
[pvc showButtonBox];
...
}
#end
Here we have the issue:
dispatch_async(bgQueue, ^{
I assume, bgQueue stands for "background queue", which means this is not served by the main thread (the UI thread).
Having that said, it's quite naive to expect
[pvc showButtonBox];
to work properly. Just move this code into the main thread. For instance, you can just wrap the aforementioned line of code into a dispatch_async on the main queue. That should solve your probem, if your outlets and/or tags are OK. Cheers.
[[PlayViewController alloc]init];
This creates a new instance of PlayViewController. Where have you defined your outlets and views?
In a storyboard? You can't use this initialiser - nothing from the storyboard will be picked up, you have to use a segue or initializeViewControllerWithIdentifier:.
In a xib file? Is it called PlayViewController.xib? If not, it won't be picked up by the initialiser. Plain alloc/init of a view controller will only find a nib file as described in the documentation of the nibName property.
Do you really want alloc / init at all? Do you actually want to make a new view controller, or is one already on the screen?
From your comments it seems option 3 is the right answer. The PlayViewController is already on the screen, alloc/init is creating a new instance of it, which is never being put on screen, which never loads any views regardless of storyboards or nibs.
You need to get a reference to the existing instance of PlayViewController. Without knowing the structure of your app it's not too easy to say how that's done - is it presenting the game view? Is the game view a subview of the view controller's view? You may need to pass in a reference (weak) to the game view when it is created, at viewDidLoad, or set up an outlet in the storyboard.
I have two scenes that I made in SpriteBuilder, one is Shop and the other is UpgradesNew. Shop is a CCNode layer and UpgradesNew is a CCNode layer. I have two CCScrollViews in the MainScene that load Shop and UpgradesNew respectively.
When one button in Shop is tapped, the label in UpgradesNew should change colors. I have been trying to implement this using delegates but it's not working.
Here's what I did...
In shop.h I set the protocol:
#protocol changeColorProtocol <NSObject>
#required
-(void)changeColor;
#end
Then I set the id
#property (nonatomic, retain) id <changeColorProtocol> delegate;
Here is the button that when clicked, should use changeColor. This is in shop.m
-(void) buyDiggerShibe {
[self.delegate changeColor];
[self didLoadFromCCB];
}
Now in UpgradesNew.h I made it adopt the protocol like this
#interface UpgradesNew : CCNode <changeColorProtocol>
And in UpgradesNew.m
I set delegate to self in ViewDidLoad.
Shop *shop = [[Shop alloc]init];
shop.delegate = self;
.
-(void)changeColor {
if (hasDigger == YES) {
shovelRequires.color = [CCColor greenColor];
NSLog(#"HEY HEY HEY");
}
}
I probably have parts of the delegate placed in the wrong area because I was trying to switch them around when it wasn't working, I'm not sure where they are supposed to go. I've watched multiple delegate tutorials and it just seems overly complicated, at least with what I am trying to do.
Any ideas?
EDIT:
Tried this.
I created a property in UpgradesNew
#property (strong, nonatomic) Shop *shop;
Then I synthesized it in the implementation and allocated it like this in didLoadFromCCB, instead of creating a new object:
self.shop = [[Shop alloc]init];
shop.delegate = self;
EDIT: This is how I am creating objects.
Drag a label into a layer. Identify it then define it in header as CCLabelTTF *label; That's it, thats all I do to create any object on the layer.
To create a layer like Shop or UpgradesNew, I hit New -> File -> Layer. That creates a new CCNode. Then I set the class of the CCNode, as shown in the picture the CCNode that is highlighted has a class of MainScene. If I want to establish a #property to that CCNode I just type the name in the box right below custom class and set it as doc root var, and then put it in the header as CCNode *MainScene. I don't do anything other than that.
I don't know anything about SpriteBuilder, so it's a bit hard to address your question. You might want to add SpriteBuilder to the title of your post so people who use that framework are likely to read it.
You need to explain how the 2 "CCNode layer"s are created, and how you link them together. In order for one object to have another object as a delegate, the delegate property has to be set somewhere. Where is that setup being done? Have you set a breakpoint at the line
[self.delegate changeColor];
To make sure that self.delegate is not nil?
Have you set a breakpoint in your changeColor method, or added a log statement, to see if it's being called? My guess is that self.delegate is nil, so the messages is being dropped on the floor (it's legal to send messages to nil in Objective-C. It just doesn't do anything.)
Being new to objective-C coding I started out writing a basic app, fully programmatically (not using storyboards or xib) in one file, my AppViewController h and m files.
Everything worked lovely.
So then I wanted to break up the mass of code by subclassing sections, and everything went well apart from the UIPickerView. In fact simply commenting out the [background addSubview:colorPicker]; seemed to totally fix the issue. I never found the answer online so I proceeded to make a new document to replicate said issue.
So here goes:
UIPickerViewController.h
#import <UIKit/UIKit.h>
#import "Picker.h"
#interface UIPickerViewController : UIViewController
#end
Simply imports my new class.
UIPickerViewController.m
#import "UIPickerViewController.h"
#interface UIPickerViewController ()
#end
#implementation UIPickerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *superview = self.view;
int height = superview.bounds.size.height;
int width = superview.bounds.size.width;
CGRect popupRect = CGRectMake(0, 0, width, height);
UIView *popup = [[UIView alloc]initWithFrame:popupRect];
popup.tag = 8;
[superview addSubview:popup];
Picker *picker = [[Picker alloc]initWithFrame:popupRect];
[picker viewAddTypeScreenToView:superview];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
Sets up a new view with a tag (so that i could reference it later with my new class)
Then actions a method from my new class to populate my new view.
Picker.h
#import <UIKit/UIKit.h>
#interface Picker : UIView
<UIPickerViewDataSource,UIPickerViewDelegate>
{
UIPickerView *colorPicker;
NSMutableArray *colorsArray;
}
#property (nonatomic, retain) UIPickerView *colorPicker;
#property (nonatomic, retain) NSMutableArray *colorsArray;
#property (strong,nonatomic) UILabel *myValue;
-(void)viewAddTypeScreenToView:(UIView*)superview;
#end
Setting up my variables and accessible method.
Picker.m
#import "Picker.h"
#implementation Picker
#synthesize colorsArray;
#synthesize colorPicker;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)viewAddTypeScreenToView:(UIView*)superview
{
UIView *baseView =[superview viewWithTag:8];
int height = baseView.bounds.size.height;
int width = baseView.bounds.size.width;
CGRect fullScreen = CGRectMake(0, 0, width, height);
UIView *background = [[UIView alloc]initWithFrame:fullScreen];
background.backgroundColor = [UIColor blackColor];
colorsArray = [[NSMutableArray alloc] initWithObjects:#"Red",#"Blue",#"Yellow",#"Green",nil];
CGRect myPickerRect = CGRectMake(10, 70, (width/2)-40, 200);
colorPicker = [[UIPickerView alloc]initWithFrame:myPickerRect];
colorPicker.dataSource = self;
colorPicker.delegate = self;
colorPicker.showsSelectionIndicator = YES;
[colorPicker selectRow:2 inComponent:0 animated:YES];
CGRect labelFrame = CGRectMake(10, 10, 180, 50);
_myValue = [[UILabel alloc]initWithFrame:labelFrame];
_myValue.textColor = [UIColor redColor];
_myValue.text = #"select colour";
[background addSubview:_myValue];
[background addSubview:colorPicker];
[baseView addSubview:background];
}
-(NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView{
return 1;
}
-(NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component{
return colorsArray.count;;
}
-(NSString *)pickerView:(UIPickerView *)pickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
{
return colorsArray[row];
}
-(void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component{
_myValue.text = [NSString stringWithString:colorsArray[row]];
}
#end
And finally the initiation called by the method in the picker class file.
This gives me an error along these lines
-[UITableViewCellContentView pickerView:titleForRow:forComponent:]: unrecognized selector sent to instance 0x8f2b000
2014-03-19 10:29:48.407 Briefcase[1800:60b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UITableViewCellContentView pickerView:titleForRow:forComponent:]: unrecognized selector sent to instance 0x8f2b000'
Which i've read is to do with either the datasource, or ARC systems, however none of the responses that I have found relate to or work with the type of set up that I have above. I'm sure it's something really simple but after a few days of failed searching, it's officially driving me crazy.
The problem is most likely that the instance of Picker that is being created in UIPickerViewController is never added to the view hierarchy and thus gets released prematurely (provided we're talking about a project using ARC here).
This leads to the pickerview's delegate and datasource becoming invalid and, basically, pointing at any random object. That's what is causing your crash: A message to your delegate cannot be delivered because the delegate is dead already. The picker still keeps a pointer which used to point at the delegate, but which has become invalid and points at a random object now, in this case a table view cell, which basically doesn't know what to do with this message and crashes.
The problem should go away if you add Picker *picker as an ivar or a retaining / strong property to UIPickerViewController.h - this will retain the picker beyond the scope of the viewDidLoad method and should keep it alive.
But that would be just a workaround, the real problem is your overall design. You said you're new to objective-c and indeed, it looks like you lack a basic understanding of iOS view and view controller hierarchies and, to some degree, the concept of object oriented programming. You might want to dig into something more basic before trying to fix your code because, quite frankly, it should be rather re-written than fixed.
I'd be happy to provide you with suggestions about how to structure your code, but please provide some information about what functionality you'd like to achieve first.
Edit (in response to your comment):
As a rule of thumb, do not spread functionality over several classes unless necessary. For objects, which serve a rather infrastructural purpose, like a specialized textfield or a pickerview, always ask yourself: "If I would like to reuse that object in another project, would that be as easy as using any other existing object, like, for example, UILabel?" If the answer is "No", then something is wrong. Ideally, interface objects are self-contained and to use them, you just invoke them, add them to a view and tell them, which text to display or which options to offer. If that information is subject to change or if the object needs to interact with other parts of your code, make use of delegation and protocols. Under no circumstances should the functionality of your object be tied to hard coded values or rely to some view to have a certain tag.
If you subclass UIView, the resulting object should behave like any other instance of UIView. It should be added to the view hierarchy by you or some object, but it shouldn't add or remove itself. If it works without being added to the view hierarchy at all, something is wrong. A view serves the purpose of being a part in your interface and all the logic it contains should work to that end, not more, not less.
Normally, interface objects should not interfere with one another. If something happens to one object (button pressed, option selected, text changed...) and another object is supposed to reflect that change, it is the view controllers responsibility to make that happen. The view controller is the place where the logic happens. If there is a task which requires a lot of complex logic, it might be a good idea to encapsule that logic into a purpose build class. One such example would be a class which manages network connections. This class should be again self contained: If the view controller needs some remote information, it asks your network class. Once your network class has that information (or failed to retrieve it), it reports back to your view controller. The view controller then updates the interface - under no circumstance should the networking class contain code which affects the interface.
It is important to understand that you could very well ignore these rules and still end up with a working app. And in some cases, the "direct" way may appear to be easier to implement and thus may look very tempting. But you'll pay the price later - once you start debugging your code. If your picker does not behave the way it should, you need to look into several places and wrap your mind around several objects, just to make one interface object behave right. And likely you will break one functionality while fixing the other.
So, try to make it right from the start, even though it requires more planning and learning. Trust me, it pays out, I started out just like you several years ago ;)
I want to perform undo operations on the graphics context in my drawing app. When undo is pressed i want to move to the previous context that contains the old drawing.
For Example:
I have a rectangle in the context. On dragging, I move the rectangle to a new position and redraw it. Now when I press the undo button I want to move the rectangle to the previous position. How can i do this?
I have just basic idea about NSUndoManager.
Please Help!
Thanks.
The functionality you're inferring doesn't really exist. Put differently "Graphics contexts don't work this way (for free)." You may have seen the functions UIGraphicsPushContext and UIGraphicsPopContext, but they don't do what you are talking about here. The "push" and "pop" operations they represent operate on the not-yet-rendered aspects of the graphics context: the current fill and stroke colors, the clip rect, and the transform matrix for example. Once something is rendered into the context -- i.e. a path/rect/etc. has been stroked or filled, or an image has been composited into the context -- it's rendered forever. The only way to "go back" is to somehow re-create the previous state. Unfortunately, there is no magic built-in to UIGraphicsContext that will make that happen for you.
There are several ways to go about this. As another user mentioned, you can capture the state of the context after every user action. In short, this means that your document "model" is a bitmap stack. This will get very memory intensive very quickly -- bitmaps are memory-heavy. There are certainly optimizations you can make to this approach to get more mileage out of it, like only saving the region that has changed between each frame, compressing the bitmaps, swapping them out to disk, etc. There are real, usable apps in the App Store that work using this approach, but the approach is inherently limited, and you will end up expending non-trivial effort in optimizing and managing your saved stack of undo states.
There are, naturally, other approaches worth considering. The simplest is to have your actual document model be a stack of small (i.e. non-bitmap) data structures that describe the operations necessary to recreate the state of the context. When the user undoes, you simply remove the operation at the top of the stack and recreate the graphics context by playing back the remaining stack. This is a decent approach for "additive" type applications (think "Brushes") but begins to fall down even in the simple scenario you describe of moving a shape on a canvas. It also ultimately suffers from some of the same issues as the bitmap stack approach -- the more operations you have, the longer it takes to recreate the state, so you inevitably end up making bitmap snapshots periodically, etc.
For object-on-canvas scenarios like you described (the 'move shape' operation), there are also multiple approaches. One classic approach would be to have your document model be a set of smaller, more specific data structures which describe the current state of the canvas. Think of a Shape class, etc. In the simplest case, when a user adds a rectangle to the canvas, a Shape instance is added to a z-ordered array of shapes. Any time the list of shapes changes, the context gets regenerated by drawing each shape in Z order. To achieve the undo functionality, every time you mutate the array of shapes, you also use the NSUndoManager to record an invocation that, when played back, would cause the inverse operation to occur.
If your shape-add operation looked like this:
[shapeArray insertObject: newShape atIndex: 5];
Then you would, at the same time, do this with the NSUndoManager:
[[undoManager prepareInvocationWithTarget: shapeArray] removeObjectAtIndex: 5];
When the user clicks undo, the NSUndoManager plays back that invocation, and the shapeArray is returned its prior state. This is the classic NSUndoManager pattern. It works well, but also has some disadvantages. For instance, it's not necessarily straightforward to persist the undo stack across app terminations. Since app terminations are commonplace on iOS, and users generally expect an app to seamlessly restore state across terminations, an approach in which your undo stack won't survive app terminations may be a non-starter, depending on your requirements. There are other, more complex approaches, but they are mostly just variations on one of these themes. One classic worth reading about is the Command Pattern from the Gang of Four book, Design Patterns.
No matter what approach you choose, this sort of application will be a challenge to develop. This graphic undo functionality is simply not something that's built into UIGraphicsContext for "free". You've asked several times in the comments for an example. I'm sorry to say, but this is a complex enough concept that it's unlikely to be feasible for someone to provide a working example within the limitations of a StackOverflow answer. Hopefully these ideas and pointers are helpful. There are also certainly any number of open source drawing applications that you could look at for inspiration (although I'm not personally aware of any open source drawing applications for iOS.)
UIGraphicsContext doesn't have it's own undo stack. You need to store each element of what you're drawing on a stack, and remove and add items from that stack to undo and redo. The NSUndoManager class can assist you with managing the logic for the undo and redo operations themselves, but its your responsibility to write the code that saves drawing actions to a stack and then reads from it to recreate the drawing in -drawRect:.
First set the undomanager object and initialise it for usage.
NSUndoManager *undoObject;
undoObject =[[NSUndoManager alloc] init];
Register the undo object with the target function for saving the uigraphics context, every time the context is changed.
[[undoObject prepareWithInvocationTarget:self] performUndoWithObject:currentContext withLastpoint:lastPoint andFirstPoint:firstPoint];
Write the userDefined function definition
-(void)performUndoWithObject:(CGContextRef )context withLastpoint:(CGPoint)previousLastPoint andFirstPoint:(CGPoint)previousFirstPoint
{
// necessary steps for undoing operation
}
Specify action when an undo button is clicked.
-(void)undoButtonClicked
{
if([undoObject canUndo])
{
[undoObject undo];
}
}
How to perform undo and redo operation in objective c for store the value array in iOS. and i have create the small demo in Undo and Redo Button work in string value replaced.
Create the two NSMutableArray in ViewController.h Files
#interface ViewController : UIViewController<UITextFieldDelegate>
{
NSMutableArray *arrUndo, *arrRedo;
}
#property (weak, nonatomic) IBOutlet UIButton *btnUndo;
#property (weak, nonatomic) IBOutlet UIButton *btnRedo;
#property (weak, nonatomic) IBOutlet UITextField *txtEnterValue;
#property (weak, nonatomic) IBOutlet UILabel *lblShowUndoOrRedoValue;
#property (weak, nonatomic) IBOutlet UIButton *btnOK;
- (IBAction)btnUndo:(id)sender;
- (IBAction)btnRedo:(id)sender;
- (IBAction)btnOK:(id)sender;
#end
Implement the ViewController.m Files to add the two button action method in undo or redo.
- (void)viewDidLoad {
[super viewDidLoad];
self.txtEnterValue.delegate = self;
arrUndo = [[NSMutableArray alloc] init];
arrRedo = [[NSMutableArray alloc] init];
}
-(BOOL)canBecomeFirstResponder {
return YES;
}
- (IBAction)btnOK:(id)sender {
NSString *str = [NSString stringWithFormat:#"%#", [self.txtEnterValue.text description]];
self.txtEnterValue.text = nil;
self.lblShowUndoOrRedoValue.text = str;
[arrUndo addObject:str];
NSLog(#"Text Value : %#", [arrUndo description]);
}
- (IBAction)btnUndo:(id)sender {
if ([arrUndo lastObject] == nil) {
NSLog(#"EMPTY");
}
else
{
[arrRedo addObject:[arrUndo lastObject]];
NSLog(#"ArrAdd %#", [arrUndo description]);
[arrUndo removeLastObject];
for (NSObject *obj in arrUndo) {
if ([arrUndo lastObject] == obj) {
self.lblShowUndoOrRedoValue.text = obj;
}
}
NSLog(#"ArrText %#", [arrUndo description]);
}
}
- (IBAction)btnRedo:(id)sender {
if ([arrRedo lastObject] == nil) {
NSLog(#"EMPTY");
}
else
{
[arrUndo addObject:[arrRedo lastObject]];
for (NSObject *obj in arrRedo) {
if ([arrRedo lastObject] == obj) {
self.lblShowUndoOrRedoValue.text = obj;
}
}
}
[arrRedo removeLastObject];
}