If I have a uitableview within a uitableview (yes the cells of the toplevel tableview are tableviews, but never mind that) on a tabbar tab and the user selects a row how can I get access to the selected item from my appdelegate and from the tabbar tabs
Mainly the question I have is if you have a var somewhere deep in your view heirachry how can a tabbar viewcontroller or the appdelegate get hold of it?
I usually use specific class to hold such variables. The source looks something like this:
static UIView *__someView = nil;
#implementation VariableContainer
+ (void)setSomeView:(UIView *)someView {
__someView = someView;
}
+ (UIView *)someView {
return __someView;
}
#end
When you create some object you want to access from some distant object you just call [VariableContainer setSomeView:] and when you need to access it [VariableContainer someView].
This is just one of many ways you can achieve this same result. A few tips about this one:
for every custom object (such as your app delegate) put in header "#class objectName"
try importing this container only into source files (no headers)
do not use retain, only have weak links
you can put as many objects as you wish so have only 1 container
I hope this helps a bit.
Related
I'm looking for a way to show a UIView "InventoryView" in 2 view controllers.
I'm working on an inventory system for my game that I trying to make but I need to be able to access it from my main view, where it will go to a InventoryViewController (in this ViewController is my InventoryView) but I also need to be able to access the InventoryView from my BattleViewController where it does not go to my InventoryViewController but where it print the InventoryView on my BattleViewController so I can access everything durning the battle.
Example:
(evrything is dragand drop, the UIView and the UIButtons)
InventoryViewController
class InventoryViewController: UIViewController {
class InventoryView: UIView {
//here are some UIButtons and labels
}
}
BattleViewController
class BattleViewController: UIViewController {
class InventoryView: UIView {
//it should print the Inventory Screen on my BattleViewController
//here are the same properties as it shows in the InventoryViewController
}
}
This is a great example to look at the way OOP programming works best.
Ask yourself the following questions:
What is the purpose of the view?
Are the interactions on the view homogenous across all the instances? (touch events, specific behavior, etc...)
What is the minimum amount of information you need to make the view look the way you want?
Once you have those answers, you can approach the concept of reusability of views safely.
The way to go about it is to subclass UIView, create the necessary elements of your view, setup your constraints (still in the view, either in a nib or programmatically), and implement any behavior that will be consistent across views (For example if the view is a segmented control, every time you click a segment all the others go grey and the one you clicked go blue. Since that's the primary purpose of the segmented control, the code for it should belong to the segmented control).
Chances are you will find the docs very useful: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/
Lastly write a setup method that takes all the information you need and sets up all your graphical elements accordingly. Remember, views should never own their data (they should be templates, the controller will provide the data).
I have no idea what you view looks like but I assume the inventory is represented as an object. Then something like could be a good start:
class InventoryView: UIView {
var inventory: Inventory? {
didSet {
if let newInventory = inventory { //in case we set it to nil
setup(withInventory: newInventory)
}
}
}
private func setup(withInventory inventory: Inventory) {
//do your setup here
}
}
Then in your controller you can call:
let inventoryView = InventoryView()
inventoryView.inventory = myPlayer.inventory
You cannot use a view in two places, at least not within the UI. Every view can be added to only one super view at a time.
If you need the same contents to be displayed twice, create a UIViewController class which's view contains the common UI, create two of those and add them to your UI.
My app has a search view(search bar) which is used all over the app. I don't want to create duplicated code so I created a view controller called MySearchViewController to handle the search job, then I created a singleton object in AppDelegate. In every view controller, I added my search view like this:
- (void)viewDidLoad
{
MySearchViewController* search = [AppDelegate searchViewController];
[self.view addSubView:search.view];
}
My questions, Is it a good way? It's a singleton so it can be added to many views. Do I need to remove the view from last view before adding to current view?
Understand that you are mixing some concepts that are not necessarily related: avoid duplicated code and Singletons.
Wikipedia says this about singletons:
In software engineering, the singleton pattern is a design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system. The concept is sometimes generalized to systems that operate more efficiently when only one object exists, or that restrict the instantiation to a certain number of objects. The term comes from the mathematical concept of a singleton.
The most important characteristic of a singleton (in my humble opinion) is that the object is instantiated only once and every single place in your application will use the same instance. Well, to use your search feature everywhere and avoid duplicated code you don't need the search view to be instantiated only once, maybe the data that comes with it, but not the view itself.
Two better ways of achieving this:
1 - You can create a ViewController with your search and just embed this on the other views using a Container View, you can use blocks or a delegate protocol to communicate between your controller and the view that is embedding it.
2 - You can create a Parent class of the ViewController that will include the search bar, like a SearchViewController and all the other viewControllers that needs the same feature will inherit from it.
The singleton could be useful if you are planing to share the same search data and text between all the ViewControllers of the application, but it would be a singleton only with these information, the UISearchBar and all other view elements should not be part of the singleton.
Ideally, you should instantiate a fresh instance of MySearchViewController every time when you want to add it to another view to avoid problems.
Do I need to remove the view from last view before adding to current view?
Its not required to remove it from previous super view because whenever you add this singleton MySearchViewController's view to some other view, it will automatically gets removed from last super view and now its super view is your new view where you have added it.
If you want to add a view from a different view controller, your view controller has to be that view controller's parent view controller:
- (void)viewDidLoad
{
MySearchViewController* search = [AppDelegate searchViewController];
[self addChildViewController:search];
[self.view addSubView:search.view];
}
also, make sure that when the search.view is added, it is already initialised.
Why you do not use NSObject class ?, i do not know your requirement , but if you want to store latest updated value in whole project(in execution) then you should use the singleton, but if you do not want to store value (i mean one result for whole project) then you should use NSObject derived Class. advantage is singleton consumes memory so memory will be wasted. NSObject class will be reusable and only allocated when it is required and then ARC will take care of all things. If you want to know how to create NSObject and use of it then you can give me reply.
Here is some code to load a XIB as part of a custom object with the object gets initialized.
Why are you not creating custom search component for search?
you can use this component all over the app.
also this is not creating duplicat code.
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
[[[NSBundle mainBundle] loadNibNamed:#"SearchView" owner:self options:nil] objectAtIndex:0];
[self addSubview:self.view];
self.frame = self.view.frame;
}
return self;
}
Please check below code. Hope this is work for you.
- (void)viewDidLoad
{
if ([self.view viewWithTag:123456]) {
[[self.view viewWithTag:123456] removeFromSuperview];
}
MySearchViewController* search = [AppDelegate searchViewController];
search.view.tag = 123456; // give a any random tag to view
[self.view addSubView:search.view];
[self addChildViewController:search];
}
Please make sure given tag is not assign to other object except search.view in self.view.
Thanks
I have two view's in my app. In the first view I have a table view which displays data downloaded from the Internet. The FirstViewController has a method to get the data and update the view:
- (void)viewDidLoad
{
// Create PlanGenerator
_planGenerator = [[PlanGenerator alloc] init]
[self loadPlan];
- (void)loadPlan
{
_plan = [_planGenerator getData]
// Updating the view
// Updating the table view
[self.tableView reloadData]
}
To download the data from the internet I have a class called PlanGenerator. This class has an instance method called getData, it returns an NSArray. The table view uses the instance variable _plan (array) to display data in the table view.
In the second view (controlled by the SecondViewController) you can make some adjustments on what to download. To tell these change the PlanGenerator I used the concept of class properties. Now when I changed something in the second view (actually it's just one parameter) I want to call the method loadPlan from the FirstViewController.
My first thought was to create a class method, but then I would have to creat "class properties" for every variable the method uses.
Is there an easier way to do this?
You are missing some basics.
Try this design, assuming that FirstVC is used to display data and has tableview. SecondVC (your PlanGenerator) is used to get/download data.
In SecondVC:
Create whatever property(parameter, etc. says criteria) that FirstVC will supply to decide what to download.
Create a public method getData.
In FirstVC:
Create an iVar (says _myPlanGeneartor) and allocate it.
Now from the instance of FirstVC, you have access to an instance of SecondVC (_myPlanGenerator). With that you can supply criteria parameter and request data (getData).
I have a scenario where there is a parent container view controller with a subview taking up most of the screen. This subview is used to swap out 3 different views of the same data (a map, a table and a gallery). There is a segmented control that is used to select which view of the data the user wants to view. I have an array collection of my model type in the parent container view controller and I would like to have these 3 different child view controllers each display this data in their respective views. Is there any clean way to do this without having to duplicate the data 4 times (once in the parent and 3x in the children)? I'm assuming I will have to duplicate the data, because the child should not be able to call up to the parent view controller to access its array. It's also not an appropriate inheritance situation, since the parent is more of a container than the same type of view controller. It's also not a delegate situation, because the children don't need to notify the parent of anything, but the other way around.
Any suggestions greatly appreciated.
Thanks.
I would create a class (MyDataController below) to manage the data, and use a shared instance to access it from anywhere in my app.
interface (MyDataController.h)
#interface MyDataController : NSObject {
NSMutableArray *myData; // this would be the collection that you need to share
}
+ (MyDataController*)sharedDataController;
// ... add functions here to read / write your data
#end
implementation (MyDataController.m)
static MyDataController* sharedDataController; // this will be unique and contain your data
#implementation MyDataController
+ (MyDataController*)sharedDataController
{
if (!sharedDataController)
sharedDataController = [[[MyDataController alloc] init] autorelease]; // no autorelease if ARC
return sharedDataController;
}
// ... implement your functions to read/write data
#end
Finally, to access this static object from anywhere:
MyDataController *dataController = [MyDataController sharedDataController]; // this will create or return the existing controller;
You could put the data in a singleton class and just have each of your child view controllers get the data from the singleton.
I have a app, where I've a tabview controller. All the data is dynamic, and when I enter on one tab, the data is loaded, but if I change my tab and come back to the initial tab, I haven't lost the data on it, what is awesome. My problem now is that I've built a new viewcontroller (outside the tabs) and when I go into it, and come back to the tabs I've lost all my information!
Is there any way to retain the initial data? So there when the user goes to that another view, and comes back, don't have to lose the data.
And another question. Is there anyway, to define variables that are available to every viewcontroller's in the app?
Data will not change when you move from one tab to the other
You will need to check if you have some special code in your viewWillAppear, if you load the data in this function you should know that viewWillAppear gets called when you travel tabs
About the global Data, you could define them in your appDelegate class, add properties to the appDelegate and then you can access them like this
//Add this on the header of your class
#import "MyAppDelegate.h"
//Then access the delegate like this
MyAppDelegate *myAppDelegate = [UIApplication sharedApplication].delegate;
//Access your variables
myAppDelegate.myVariables;
What is this general data? If it is objects, I would call retain. But if it was a data type, try making is static and make a method returning it. Or you could wrap it in an object, (like NSNumber for example if it was a float, double or int etc.) then call retain to that.