I am currently building my view in the viewDidLoad method, it adds various buttons that links through to another view. The issue I am having is that when a user clicks on a button and goes through to the other view they can purchase an IAP, then when the user clicks on the back button I would like the view to 'refresh' to show that this purchase has now become active.
How do I refresh the view?
Thanks in advance
any view can be explicitly refreshed by calling setNeedsDisplay on that view.
Further you should not mix up the terms UIViewController with UIView.
I asume you dont have a refresh problem, that you would only have in self programmed custom views. I excpect that the data of the related views was not updated.
You have a few options. Either store whether the purchase was made and then run the method that checks if a purchase was made in the viewWillAppear method of the view you are wishing to change
Or...
Setup a delegate callback that changes the button when a change is made on the purchase page (http://mobile.tutsplus.com/tutorials/iphone/ios-sdk-custom-delegates/)
Or...
Manipluate the previous view directly by accessing it from the navigationController stack ([[self.navigationController viewControllers]objectAtIndex:YOURVIEW])
build view in viewDidLoad so you add controls only once, but refresh in viewWillShow. Keep in mind, viewWillShow will refresh it right after the build is done by viewDidLoad. So keep values that define your controls appearance on the 1st viewController, say, in delegate mutable array
e.g. create and initiate an NSMutableArray in your AppDelegate.h and .m
in .h interface
NSMutableArray *yourArray;
add property
#property (retain, nonatomic) NSMutableArray *yourArray;
in .m
#synthesyze yourArray;
initiate it in .m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
yourArray=[[NSMutableArray alloc]init];
//add initial objects to it as many as you have controls
}
in the 1st viewController
#import "AppDelegate.h"
-(void)viewWillShow:(BOOL)animated{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//pull objects from array and apply to your controls, say you have a string as a 1st obj
NSString *tmpStr=[appDelegate.yourArray objectAtIndex:0];//use it some where, say button's title
//etc
}
you will change those values in the second viewController according to users choices
#import "AppDelegate.h"
somewhere in 2nd viewController code update values in yourArray say on a
-(void)viewWillDisappear:(BOOL)animated{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//run a loop and update all objects
[appDelegate.yourArray replaceObjectAtIndex:i withObject:newObj];
}
and when you go back to 1st viewController, viewWillShow will refresh them from your delegate array.
Related
I am trying to create Singleton that it would be initialised from AppDelegate. The purpose is to monitor all the UIViewControllers (the active one) and print on console the kind of class (as proof of concept). So my basic idea is to initialise the singleton in AppDelegate and pass as parameter a reference of AppDelegate. Then somehow I must monitor which one is the active view.
For example: View A B C
A is the first view in Navigation Controller. My Singleton knows that the current view is A. Then we push view B and Singleton is notified that view B is now the current view. Same with C. Now we pop C and Singleton knows that the current view is B.
Is any kind KVO or NSNotification for notifying my singleton that a new UIView is appeard/removed? Any alternatives for this problem?
After registering for all notification I found out about UINavigationControllerDidShowViewControllerNotification.
With this observer:
[notifyCenter addObserver:self selector:#selector(viewAppeared:) name:#"UINavigationControllerDidShowViewControllerNotification" object:nil]; I am able to monitor the activity of the UINavigationController.
You can get current View controller by just making a view controller object in appdelegate like
#property (strong, nonatomic) UIViewController *currentViewController;
and then on the view will Appear of your current view controller give the current reference to the app delegate object like
AppDelegate *myAppd = (AppDelegate*)[[UIApplication sharedApplication]delegate];
myAppd.currentViewController = self;
This way you get your current active view.
One approach is to pick a particular method you want to know about and intercept it.
Here, I create a category on UIViewController and provide a method I want called whenever the controller's viewWillAppear: would normally be called:
#include <objc/runtime.h>
#implementation UIViewController (Swap)
+ (void)load
{
NSLog(#"Exchange implementations");
method_exchangeImplementations(
class_getInstanceMethod(self, #selector(viewWillAppear:)),
class_getInstanceMethod(self, #selector(customViewWillAppear:)));
}
- (void)customViewWillAppear:(BOOL)animated
{
// Call the original method, using its new name
[self customViewWillAppear:animated];
NSLog(#"Firing %# for %#", VIEW_CONTROLLER_APPEARED, self);
[[NSNotificationCenter defaultCenter] postNotificationName:VIEW_CONTROLLER_APPEARED
object:self];
}
#end
After that, it's just a case of listening for the notification in whatever object needs to know (e.g. your Singleton).
I have an app that includes a bluetooth LE class for handling a BT connection. When a characteristic value is changed I want code in the BT class to change a button state on the main viewcontroller. I'm new to obj-c and usually use stackoverflow to help with my understanding. I have the following defined in my viewcontroller:
#interface myViewController : UIViewController<CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate, UINavigationBarDelegate, UIActionSheetDelegate>
#property (weak, nonatomic) IBOutlet UIButton *eraseButton;
within the view controller, I can happily change the button state:
[self.eraseButton setHidden:YES];
and
-(void) deselectEraseButton
{
[self.eraseButton setSelected:NO];
}
within the BT class I have tried many things (changing a property of viewcontroller which changes the button state via a timer, calling a method that directly changes the button state etc) and although the code is executed (I have breakpoints set on the code in myViewController), the button state isn't changed. For example:
myAppDelegate *app = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
app.vCtrl=[[myViewController alloc] init];
[app.vCtrl deselectEraseButton];
What am I doing wrong and what's the best way of doing this? I'm sure it shouldn't be this hard!
UPDATE:
with the help of Zhi-Wei Cai (thanks), I'm made some changes but alas, it's still not working:
I added the following to myAppDelegate.h:
#property (strong, nonatomic) myViewController* vCtrl;
I added the following to myAppDelegate.m:
-(void)eraseButton:(BOOL)state{
[vCtrl accessEraseButton:state];
}
From the BT class, I do the following:
[self performSelectorOnMainThread:#selector(test) withObject:nil waitUntilDone:YES];
with the following method in that class:
-(void) test
{
myAppDelegate *app = (myAppDelegate *)[[UIApplication sharedApplication] delegate];
[app eraseButton:0];
}
This called myViewController and on thread1 did the following:
-(void) accessEraseButton: (BOOL) state
{
if (state==0)
{
[self.eraseButton setSelected:NO];
[self.view setNeedsDisplay];
}
else
{
[self.eraseButton setSelected:YES];
}
}
I then added [self.view setNeedsDisplay]; for good luck. The button is still not changing state but the code is running (I can hit a breakpoint and it breaks on the method and the method is running on thread1)?!? Any ideas?
* UPDATE 2 *
Ok, I've worked out what I was doing wrong. I was doing all of the above without properly referencing the viewcontroller. So although the code was executing, the instance of the viewcontroller wasn't correct and all the button handles were nil. I needed to add the following to myViewController.m, in viewDidLoad:
myAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.myViewController = self;
Now it works. Happy days...
Because when you do app.vCtrl = [[myViewController alloc] init];, you actually created a new instance of the UIViewController, not the original one that's on your screen.
I didn't test them, but you can achieve what you wanted by:
Via Interface Builder: Setup an IBOutlet of the UIViewController in your myAppDelegate, connect it to the ViewController within Interface Builder, then create a method in myAppDelegate to change it or access it directly using myAppDelegate as it's a property. You can now call it within your BT class e.g. [app deselectEraseButton] or [app.myViewController deselectEraseButton] or even do something with the button if the button is a property of the AppDelegate/VC.
Via Delegate: Create a #protocol to setup a delegate for the BT class, so the it can do callback within other classes such as myAppDelegate.
Via NSNotificationCenter: Use - (void)addObserver:(id)notificationObserver selector:(SEL)notificationSelector name:(NSString *)notificationName object:(id)notificationSender and - (void)postNotificationName:(NSString *)notificationName object:(id)notificationSender userInfo:(NSDictionary *)userInfo to do it. This will work app-wide.
I believe the above three already has tons of code example on SO, just search them and you will get what you need.
I've updated by original question with 2 updates that lead to a final understanding and solution. Hope it helps others who may be similarly confused!
I am trying to learn iOS development but have stalled a bit so I hope that there is some kind soul here who might be able to help me in the right direction.
Let's say I have a UITableViewController that displays a number of items, consisting of a title and subtitle ( Subtitle style of a Tableview Cell). Items.m/h only consist of two properties, title and subtitle and a init method to set the properties. In my app delegate i create some default items and pass them/set them to my tableViewController's property tvc.items, which is a NSMutableArray. What do I need to do / what components do I need, to be able to add more items and then display them in my tableViewController?
I started with the following:
Added a new view controller in the storyboard
Embeddade the viewController in a Navigation Controller
Added a Bar Button Item at my Table View Controller with an identifier of add
Ctrl + drag from BarButtonItem (add) to my new view controller selected modal segue
Created a new class AddNewItemViewController
Entered this as the class under the Identity Inspector for the new view controller
I then added two Bar Button Items, Cancel and Done (with cancel and done as identifiers) in the storyboard for the new View Controller
This was followed by me adding two UITextFields, one for the Title and one for the Subtitle
Ctrl + drag from these outlets into AddNewItemViewController.m, between #interface AddNewItemViewController () ... here ...#end (so they become Private? Should I drag it here or to AddNewItemViewController.h ?, What is the standard way for doing similar outlets?).
In AddNewItemViewController I added two properties, NSString's (nonatomic, copy) * title and *subtitle which I thought would keep the input data from an intended user.
So, after this I now want do two things, and it is here as it becomes difficult (for me at least):
Making so that by clicking on Cancel, one return to the Table View controller, ie a dismissed the modal .
Adding the data within the text fields to that NSMutableArray which is the datasource by clicking Done.
So what is required of me to do the last two steps?
Where should I ctrl + drag from the Cancel and Done (so there will be actions)? I guess they must be submitted to AddNewItemViewController.m, but what must be done to dismiss the modal (by clicking on the 'Cancel') and what should be called at or performed when clicking on 'Done'?
Which or what class (es) must know about the other class?
Last but not least, what should I send in the prepareForSegue call (which I guess I will need to have to use to send the input data back to the table view controller)?
Where to start and what methods should i learn about in order to achieve my mission?
Best Regards,
Rookie
much quesetions :)
I will beginn with the close action.
Have a look at the AppleDocumentation, dismissViewController with sender self (your AddViewController).
To store your data from AddViewController to your TableViewController, it's a better way to use delegation.
AddViewController.h
#protocol AddViewControllerDelegate;
#interface AddViewController : UIViewController
#property (nonatomic, weak) id<AddViewControllerDelegate>delegate;
#end
#protocol AddViewControllerDelegate <NSObject>
- (void) addViewControllerDidFinishTakingData:(AddViewController *)addViewController withTitle:(NSString *)title andSubtitle:(NSString *)subTitle;
#end
AddViewController.m
- (IBAction)done:(id)sender
{
NSString *title = ...;
NSString *subtitle = .. .;
[self.delegate addViewControllerDidFinishTakingData:self withTitle:title andSubtitle:subtitle];
}
TableViewController.m
#interface TableViewController ()<AddViewControllerDelegate>
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"yourIdentifier"])
{
AddViewController *addViewController = (AddViewController *)segue.destinationViewController;
addViewController.delegate = self;
}
}
Last but not least to implement your new delegate-method
- (void)addViewControllerDidFinishTakingData:(AddViewController *)addViewController withTitle:(NSString *)title andSubtitle:(NSString *)subTitle
{
// handle your data here (store to array)
// reload your table
}
Better way, to create a Class (Model) for every entry.
The simplest thing to do would be to assign tvc.items to the destinationViewController's property during prepareForSegue.
You are correct in thinking that the Cancel and Done buttons belong to the AddNewItemViewController.
In the action for Done, you could add the new item to the items array you passed in during prepareForSegue, then in the presenting view controller (the one you launched the modal from), during viewDidAppear just reload the table. It'll be called when the modal disappears.
I got a customized UILabel which register itself to a communication instance during creation. This part works perfect, the UILabel will . But when I remove the UILabel will update itself after called by the communication instance. I have of course remove the listener delegate too. But as ViewDidUnload is not call anymore, I do not know where.
Here the code sample:
#implementation MyValueLabel
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate addBalanceListener:self];
}
return self;
}
-(void)communicationUpdate
{
self.text=[NSString stringWithFormat:#"%.02f", ((double)g_com.getRemoteValue())/100 ];
}
// This is the missing Method
-(void) DestructionOfTheLabelWhichIDidNotFound
{
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate removeBalanceListener:self];
}
#end
Technically you seem to be looking for the -dealloc method which is called on an object when it is to be deallocated. However I do not believe that is an adequate solution.
The example provided here seems to be doing a number of unexpected things which I believe will result in code which is surprising to other developers, requires fighting against common patterns in the UIKit framework, and be difficult to maintain.
Normally iOS view controllers are responsible for mediating or coordinating how data reaches view objects. A controller supplies a view with either the data it should currently display or provides it with a source of that data. This allows a single view class to be used in many different locations and to display data of the same type regardless of its source. Instead here we have a view which reaches out to obtain it's own data. There's no way for a controller to determine when an instance of this view class should update or what data it should display. For example the controller cannot decide to pause updates, or cause other view elements to update at the same time.
In addition this view makes several assumptions about the source of it's data and how that source may be obtained. The view assumes that the current application's app delegate is specifically an AppDelegate class so it will not work if you want to use this view in another application. Since the view also obtains this AppDelegate instance via a call to + sharedApplication there's no hint to users of this view that it has this dependency.
Consider instead allowing classes which use this view class to provide it with the data it should display.
If the property where addBalanceListener: stores the reference is weak, you don't need to do anything. Weak properties are automatically set to nil when the instance they are pointing to is dealloced.
I am a novice Ios developer and this a my first project which is ipad specific.I am having some issues with UIPopOverControl class implementation. In my project i have a View controller subclass and In that view i have several buttons . What I need to do is after pressing a button show a Popover with UIPopOvercontrol class.The data shown in the popover would be different for tapping different button and all the data would be presented in UITableView style. I have been able to show one button and one popOver using one UIViewController and UITableViewController subclass and loading data from a nib file. But how can I perform my desired task??
Thanks in advance
In your application, not need to use multiple UIPopoverViewController, you can also do this by using single UIPopoverViewController.
simple create new Class of UIPopoverViewController;
and also create UITableView in UIPopoverViewController.
In your application you have multiple UIButton, just give tag of each buttons and pass your data base on tag of button, and display this data on UITableView.
iOS purposely restricts having only one Popover visible at a time. The API is intrinsically designed to prevent having multiple open at the same time. You will need to consider an alternative approach that satisfies your requirements.
I had made the requirement done here.
Check this out.
It will help you for sure.Code for showing 2 popViewController at the same time and different data in both
You can set a variable in your AppDelegate to check which button is pressed?
Then, In viewWillAppear of your Controller, take the reference of that variable and check which button is pressed?
Then reload the data as per requirement in cellForRowAtIndexPath.
Hope this helps
AppDelegate.h
#interface AppDelegate : UIResponder
#property int buttonPressed; // Set this on click of any button
#end
YourController.h
int buttonPressed;
YourController.m
(void) viewWillAppear:(BOOL)animated
{
AppDelegate *app = (AppDelegate *) [[UIApplication sharedApplication] delegate];
buttonPressed = app.buttonPressed;
}
(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (buttonPressed == 1) { // do this}
else if (buttonPressed == 1) { // do this}
else // do this
}