I'm trying to create a bookmark page for my app. basically, I want to be able to click on a button in my UIViewController, and have the url of the website I'm currently viewing be saved as a cell in a UITableViewController. The problem is I'm not so sure of the best way to implement this.
I think the way to do it would be to have a mutable array in my tableviewcontroller which I've created here:
#import <UIKit/UIKit.h>
#interface FavoriteViewController : UITableViewController
#property (strong, atomic) NSMutableArray *tableItems;
#end
I could then use the button in the viewController to populate the array. However when I try something like this:
- (IBAction)fave:(id)sender {
[FavoriteViewController.tableItems addObject:[NSString self.faveURL]];
}
I receive an error that property tableItems is not found on FavoriteViewController. Not sure why this is. Anyone have a solution?
Access properties through object instead of directly calling by class name.
- (IBAction)fave:(id)sender {
// If accessing from other class
FavoriteViewController *favVC = ------
[favVC.tableItems addObject:[NSString self.faveURL]];
}
- (IBAction)fave:(id)sender {
// If accessing from same class where you defined your variable
[self.tableItems addObject:[NSString self.faveURL]];
}
If you want to access using class name then you have to static/class methods instead of properties.
[self.tableItems addObject:[NSString self.faveURL]];
You're accessing a class variable with your current code, but tableItems is an instance variable. So if the function fave is in the FavoriteViewController class, then you need to reference self when accessing the instance variable.
Related
I'm creating an user setup account with 5 steps using storyboard. Each step have a ViewController: 1º)Input for Name, contact etc, 2º) Import photos, 3º)Input, etc 4º)more inputs 5º)Confirmation Page, if the user click "confirm" -> Get all the inputs and upload to Parse.
The only solution i get when i search online for this, is to create a func "Prepare for Segue" and pass the information...But for me, this doesnt make any sense:
If i had 1000 viewcontrollers, the first viewcontroller information will be passsed through all the 1000 viewcontrollers? Why not the nº1000 view controller getting all the information that was left behind? e.g: The viewcontroller nº50 dont need the information about the viewcontroller nº1... This is a nonsense.
Is there any other way to implement this?
Maybe there is a solution since i'm using Parse like when i do:
ParseClass["Name"] = textfield.text
It sends information to Parse and they hold it until i do something like SaveInBackground().
I'm trying to find a solution like this using viewcontrollers. Each viewcontroller send information to parse and hold the information until the "saveinbackground()" happens on Viewcontroller nº5.
Possible? thank you
Yes it is possible. You can use NSUserDefaults for that which will store your info into memory and once it is stored you can use it anywhere in your project.
Like you can store value with it this way:
NSUserDefaults.standardUserDefaults().setObject(yourObject, forKey: "yourKey")
Now you can retrive this info from any where with yourKey like this:
let yourInstance = NSUserDefaults.standardUserDefaults().objectForKey("yourKey")
And you can cast it's type as per your need.
For more Info read Apple Document for NSUserDefaults.
One more way to pass value from one view to another view without segue by using Class or Struct.
You can read more about Classes and Structures from Apple Documentation.
If our inputs are finite, create a model class with properties for it ex:
#interface UserDataInput : NSObject
#property (nonatomic)NSString *name;
#property (nonatomic)NSString *contactNumber;
....
blah blah bla
....
#end
then make it as a singleton class
#implementation UserDataInput
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
static UserDataInput *sharedInstance = nil;
dispatch_once(&onceToken, ^{
sharedInstance = [[[self class] alloc] init];
});
return sharedInstance;
}
#end
Then set properties from a view controller on leaving that view controller like,
UserDataInput *sharedInput = [UserDataInput sharedInstance];
sharedInput.name = self.nameField.text;
etc....
In final view controller you can access these properties to upload to parse.
Try it.
let yourInstance = NSUserDefaults.standardUserDefaults().objectForKey("yourKey")
NSUserDefaults.standardUserDefaults().synchronize()
I have an application where A View Controller (A)is called twice in close succession. Now each time it is called, an NSString object is created, and I need this value to be stored in an NSMutableArray that is a public property of ANOTHER View Controller (B).
In A, I create an instance of the second View Controller (B), and using that instance, add the NSString objects into the NSMutableArray which I've created as a public property. Later, when I am inside View Controller B and print the contents of the NSMutableArray property, the array is empty. Why? Here is the code that is inside View Controller A:
-(void)viewDidLoad {
ViewControllerA *aVC = [[ViewControllerA alloc] init];
if (aVC.stringArray == nil) {
aVC.stringArray = [[NSMutableArray alloc] init];
}
[aVC.stringArray addObject:#"hello"];
[aVC.stringArray addObject:#"world"];
for (NSString *wow in aVC.stringArray) {
NSLog(#"The output is: %#", wow);
}
}
Inside my View Controller B class, I have the following code:
- (IBAction)buttonAction:(UIButton *)sender {
NSLog(#"Button selected");
for (NSString *test in self.stringArray) {
NSLog(#"Here are the contents of the array %#", test);
}
}
Now the buttonAction method gets called, as I do see the line Button selected in the system output, but nothing else is printed. Why? One thing I want to ensure is that View Controller A is called twice, which means I would like to see in the output, "Hello World", "Hello World" (i.e. printed twice), and not "Hello World" printed just once.
The other thing I wish to point out is that View Controller B may not be called at all, or it may be called at a later point in time. In any case, whenever View Controller B is called, I would like to have the values inside the array available, and waiting for the user to access. How do I do this?
Your approach is not ideal, potentially leading to a memory cycle, with two objects holding strong pointers to each other.
You can instead achieve your goal in two ways;
Delegate Protocol
This method allows you to set delegates and delegate methods to pass data back and forth between view controllers
in viewControllerA.h
#protocol viewControllerADelegate <NSObject>
- (void)addStringToNSMutableArray:(NSString *)text;
#end
#interface viewControllerA : UIViewController
#property (nonatomic, weak) id <viewControllerADelegate> delegate;
in viewControllerB.m
// create viewControllerA class object
[self.viewControllerA.delegate = self];
- (void)addStringToNSMutableArray:(NSString *)text
{
[self.mutableArray addObject:text];
}
in viewControllerA.m
[self.delegate addStringToNSMutableArray:#"some text"];
Utility Classes
Alternatively you can use a utility class with publicly accessible methods (and temporary data storage). This allows both viewController classes to access a shared data store, also if you use class methods, you don't even need to instantiate the utility class.
in XYZUtilities.h
#import <Foundation/Foundation.h>
#interface XYZUtilities : NSObject
+ (void)addStringToNSMutableArray;
#property (strong, nonatomic) NSMutableArray *array;
#end
in XYZUtilities.m
+ (void)addStringToNSMutableArray
{
NSString *result = #"some text";
[self.array addObject:result];
}
+ (NSArray)getArrayContents
{
return self.array;
}
in viewControllerA.m
NSString *stringFromObject = [XYZUtilities addStringToNSMutableArray];
in viewControllerB.m
self.mutableArray = [[NSMutableArray alloc] initWithArray:[XYZUtilities getArrayContents]];
I'm not sure what kind of a design pattern you are trying to follow but from the looks of it IMHO that's not a very safe one. However, there are many, many ways this could be accomplished.
One thing though, you said that View Controller B may never get allocated and if it is alloc-ed, it will be down the road. So you can't set a value/property on an object that's never been created.
Since you already aren't really following traditional patterns, you could make a static NSMutableArray variable that is declared in the .m of your View Controller B Class and then expose it via class methods.
So it would look like this:
viewControllerB.h
+(void)addStringToPublicArray:(NSString *)string;
viewContrllerB.m
static NSMutableArray *publicStrings = nil;
+(void)addStringToPublicArray:(NSString *)string{
if (publicStrings == nil){
publicStrings = [[NSMutableArray alloc]init];
}
if (string != nil){
[publicStrings addObject:string];
}
}
Then it would be truly public. All instances of view controller B will have access to it. This, of course is not a traditional or recommended way of doing it—I'm sure that you will have many replies pointing that out ;).
Another idea would be to use a singleton class and store the values in there. Then, when or if view controller B is ever created, you can access them from there.
I have a UITableView in my main view and an 'add' button in a UINavigationBar that will push to another view that allows the user to add another object to the tableview. I have a protocol in this view that allows the information to be sent back to the main view.
My problem is that whenever I try to add this new object sent from the protocol (which is a NSMutableDictionary) to a NSMutableDictionary property in the main view, it does not add. I have tried adding an NSLog and it says that this object is null. If I initialise this object in the viewDidLoad method, it will run whenever the UINavigationController pops the view, and resets everything in the dictionary. I do not know where to initialise the object to make sure that it keeps everything stored in it.
The protocol works fine, but I cannot do anything with the object it sends.
In AddCellViewController.h:
#import <UIKit/UIKit.h>
#protocol AddCellDelegate <NSObject>
#required
-(void)passCellInfo:(NSMutableDictionary *)cellInfo;
#end
#interface AddCellViewController : UITableViewController <UITextFieldDelegate> {
id <AddCellDelegate> delegate;
}
#property (strong) id <AddCellDelegate> delegate;
#end
in AddCellViewController.m (the method that utilises the protocol):
-(void)sendObject{
[[self delegate] passCellInfo:newCellInfo];
[self.navigationController popToRootViewControllerAnimated:YES];
}
in MainView.m:
-(void)passCellInfo:(NSMutableDictionary *)cellInfo{
[self.cellInformation setValue:cellInfo forKey:[cellInfo objectForKey:#"cell_title"]];
[self.cells addObject:[cellInfo objectForKey:#"cell_title"]];
[self.tableView reloadData];
NSLog(#"%#: cellInfo - cellInformation: %# - cells: %#",cellInfo ,self.cellInformation,self.cells); //logs the object passed from the protocol (this works), the cellInformation object, and cells object (these return null)
}
You can use a NSMutableDictionary property in a static or global class, and access it very easy from the tableview or any other view:
You feed your table from that dictionary
And you add elements from your other view into that dictionary (the protocol is not needed anymore).
Everytime your tableView appears, you should refresh the data of the table from this global class.
Some example of how to use it:
How to implement global static class
In your code you do this. You using same key every time. So it will get replaced.
[self.cells addObject:cellInfo];
I will tell you a simple way instead. Send your mutableDictionary to secondView from init method. Copy in class level mutableDictionary. Do not allocate or initialize. Add new item in that Dictionary. It will reflect in mainView dictionary. Call [tableView reloadData] in mainView viewWillAppear method.
Im trying to make it so that every single UIControl in my application (UIButton, UISlider, etc) all have special extra properties that I add to them.
I tried to accomplish this by creating a UIControl Category and importing it where needed but I have issues.
Here is my code.
My setSpecialproperty method gets called but it seems to be getting called in an infinite loop until the app crashes.
Can you tell me what Im doing wrong or suggest a smarter way to add a property to all of my UIControls?
#interface UIControl (MyControl)
{
}
#property(nonatomic,strong) MySpecialProperty *specialproperty;
-(void)setSpecialproperty:(MySpecialProperty*)param;
#end
////////
#import "UIControl+MyControl.h"
#implementation UIControl (MyControl)
-(void)setSpecialproperty:(MySpecialProperty*)param
{
self.specialproperty=param;
}
///////////////
#import "UIControl+MyControl.h"
#implementation ViewController
UIButton *abutton=[UIButton buttonWithType:UIButtonTypeCustom];
MySpecialProperty *prop=[MySpecialProperty alloc]init];
[abutton setSpecialproperty:prop];
While you can't add an iVar to UIControl via a category, you can add Associated Objects, which can be used to perform much the same function.
So, create a category on UIControl like this:
static char kControlNameKey;
- (void) setControlName: (NSString *) name
{
objc_setAssociatedObject(self, &kControlNameKey, name, OBJC_ASSOCIATION_COPY);
}
- (NSString *) controlName
{
return (NSString *)objc_getAssociatedObject(array, &kControlNameKey);
}
There's more to it than that, I guess you'll need to check if an association exists before setting a new one, otherwise it will leak, but this should give you a start.
See the Apple Docs for more details
self.specialproperty=param is exactly the same as calling [self setSpecialproperty] (see here for some totally non biased coverage of Obj-C dot notation), which makes your current usage infinitely recursive.
What you actually want to do is:
-(void)setSpecialproperty:(MySpecialProperty*)param
{
_specialproperty = param;
}
Where _specialproperty is the implicitly created ivar for your property.
I'm assuming there's some reason why you've implemented your setSpecialproperty setter? Why not just use the one that is implicitly created for you?
the problem is that you can not add a property to a category, you can add behavior (methods) but not properties or attributes, this can only be done to extensions, and you can not create extensions of the SDK classes
use your method as
change your method name to
-(void)setSpecialproperty:(MySpecialProperty *)specialproperty
-(void)setSpecialproperty:(MySpecialProperty*)specialproperty
{
if(_specialproperty!=specialproperty)
_specialproperty = specialproperty;
}
and synthesize your specialProperty as
#synthesize specialproperty=_specialproperty;
I have a list of items showing up on a table view.
Every item has its properties such as name, pic, rank etc'.
My goal is, every time the user selects a row the item with its properties will be added to a new list.
I've created a new list called listOfBugs and because i want it to be global i've allocated and initialized it inside viewDidLoad. (Is that a proper thing to do?)
Here is my code:
MasterViewController.h
#interface MasterViewController : UITableViewController
{
NSMutableArray *listOfBugs;
}
#property (strong) NSMutableArray *bugs;
MasterViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
listOfBugs = [[NSMutableArray alloc]init];
self.title = #"Scary Bugs";
}
...
...
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ScaryBugDoc *bug = [self.bugs objectAtIndex:indexPath.row];
UIAlertView *messageAlert = [[UIAlertView alloc]
initWithTitle:#"Row Selected" message:bug.data.title delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[messageAlert show];
[listOfBugs addObject:bug];
NSLog(#"this is %#",listOfBugs);
}
Using NSLog I can see that the objects are added:
ScaryBugs[1195:11303] this is <ScaryBugDoc: 0x75546e0>
2012-12-05 17:45:13.100
ScaryBugs[1195:11303] this is <ScaryBugDoc: 0x75546e0>
I have a few questions.
1.How can I access the properties of the objects inside of the array listOfBugs ?
Update: This worked for me:
NSLog(#"this is %#",((ScaryBugDoc *)[listOfBugs objectAtIndex:0]).data.title);
But I can't access the listOfBugs from another class.
I turned it into a property as suggested to make my life easier but still can't access it from another class.
For example in listOfBugsViewController.m return [_listOfBugs count]; will give me the error Use of undeclared identifier '_listOfBugs'
2.I want to be abale to populate a table view with the customized list, how can i do that?
After accomplishing that I would like to save the list as a plist and also add and remove objects from it at ease so I need to take that under consideration.
This is the code that I'm based on, I only made a few adjustments to create the new list
This is really two questions:
1) How do I make my property a public property which can be accessed by other classes?
You do this just like you did with your bugs property. Add this to your .h file:
#property (strong) NSMutableArray *newList;
Note that if you aren't using different threads, you can make it a little more efficient by using the nonatomic property as well (#property (nonatomic, strong)).
Once you do that, you don't need your iVar declaration because it will automatically be generated for you. (i.e. you can remove NSMutableArray *newList;.)
2) How do I access an object in an array?
Objects in an array are stored as an id object, meaning that it is a "generic" object. If you know what type of object is stored, then you need to tell the compiler what it is so that it knows what properties and methods are appropriate for that class. You do this by casting the variable to the proper type:
ScaryBugDoc *bug = (ScaryBugDoc *)[self.newList objectAtIndex:0];
Then, you can access the properties of the object, assuming that they are public (as covered in point 1 above) like this:
NSLog(#"this is %s", bug.data.tile);
Okay, so based from the comments, this should work:
Album* tempAlbum = [albumList objectAtIndex:i];
//now you can access album's properties
Song* tempSong = [album.songs objectAtIndex:j];
//now you can access song's properties
This can be simplified down to:
Song* someSong = [((Album)[albumList objectAtIndex:i]).songs objectAtIndex:j];
When returning an object from an NSArray, or a collection object like that it will return a generic id object. This will need to be typecasted to the expected object so you can access the right properties.