Add a tag to NSArray object - ios

I want to create 2 arrays, and populate my table view with that arrays. Then, if user click on a cell, that contain object from first array, i want to perform transition to my detail controller "one". Therefore, if user tap on cell, that contain text from second array, i want to perform segue for detail controller "two".
How to achieve that? I can't put tag on array objects and check it.
Edit: or, if NSDictionary suitable for that case, i could use them instead.

instead of having an array of data elements, you could have an array of MyDataClass, which has attributes for your data, and to identify the source.
You can use a single array to populate the table, and as suggested have different methods for populating the table cell based on the source.

Another way to do this would be create a third array with NSDictionary objects with two keys 'tag' and 'data'. 'tag' key will hold the information about which array and 'data' key will hold data from the array.
NSMutableArray *tableArray = [NSMutableArray arrayWithCapacity:0];
for (id obj in array1) {
[tableArray addObject:#{#"tag":#1, #"data":obj}];
}
for (id obj in array2) {
[tableArray addObject:#{#"tag":#2, #"data":obj}];
}
Then use this new array to populate your table
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
....
id obj = [[tableArray objectAtIndex:indexPath.row] objectForKey:#"data"];
....
}
and on didSelectRowAtIndexpath you can check value for 'tag' key to check whether it is from array1 or array2
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
....
NSNumber *tag = [[tableArray objectAtIndex:indexPath.row] objectForKey:#"tag"];
if (tag.intValue == 1) {
//controller 1
}
else if (tag.intValue == 2) {
//controller 2
}
....
}

There are multiple ways of looking to this problem. Firstly, you have to understand that you can populate a table view with only a single array; however this array can be made from multiple arrays.
Without getting ourselves dirty into multiple data structures that might provide a lot of redundancy than efficiency, a simple way would be to check for the array number in tableView:didSelectRowAtIndexPath:.
Example:
- (void) tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath
{
<Type> obj = tableViewArray[indexPath.row];
if (obj in array1)
{
// ...
}
else
{
// ...
}
}
The tableViewArray is probably array1+array2.

Related

Passing NSDictionary to another controller using UITableView

I have this NSDictionary:
{
Name =
(
"Lucas",
"Tom"
);
Surname =
(
"Jhon",
"Smith"
"Hop"
);
Name is a simple NSArray and Surname is a NSMutableArray.
Ok now my problem is this:
Starting from this dictionary, I must show in UITableView the key of each element of the NSDictionary. For example, in this case, UITableView will have two items, one call Name and the other Surname.
When I click on an element of the UITableView I will have to go to another controller that will show me all the data of the clicked cell. If for example, I click on Surname, when I open the new controller, I'll have to get it like Jhon, Smith and Hop.
To get started, I tried to show the UITableView before all the keys by writing:
self.KeyArray = [myDictionary allKeys];
and in the cellForRowAtIndexPath method:
cell.textLabel.text = [self.KeyArray objectAtIndex: indexPath.row];
But this is a wrong approach, because I'm going to lose all the information related to that specific key. Do you have any idea how I can do it?
You can use delegate to send that NSMutableArray to another controller.
Lets say your first view controller is "A" which has those name and surname keys. And second Controller is "B" which will has the array that you want to send.
Now,
1. Create a protocol in A controller.
2. Create the delegate object of that protocol.
3. Create 1 Method in A controller which can have a parameter Like :- (NSMutableArray *).
4. Implement that protocol in to B Controller.
5. Declare the method in Controller B that you defined in Controller A
6. Call that method with the help of delegate object in didSelectRowAtIndexPath and Pass that Object in that method.
7. Now you have the NSMutableArray in that method and you are free to use that values.
You can do like that in didSelectRowAtIndexPath write below code
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *keyOfMainDic = [self.KeyArray objectAtIndex:indexPath.row]; // in the keyOfMainDic, you will get selected key name of your main dictionary
NSMutableArray *temArray = [yourMaindict objectForKey:keyOfMainDic] // You will get array of specific key.
// Here you just need to pass "temArray" to your destination controller.
}
First you should define a segue from your view controller that has tableview to your detail controller and give it an identifier in storyboard.
Then define a property into your detail controller header inherit from KeyArray type.
After that, just code it like below. This is just suggestion, please use a model for your array instead of dictionary.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
self.KeyArrayItem = [self.KeyArray objectAtIndex:indexPath.row];
[self performSegueWithIdentifier:#"yourSegueID" sender:self.KeyArrayItem];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
AnotherController *controller = segue.destinationViewController;
controller.KeyArrayItem = sender;
}

ios/objective-c: Get count of array before table loads

I am populating a tableview with the contents of an array. Right now, I create the array in viewdidload and I calculate the number of rows in the delegate method
//in viewdidload
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: kItemsURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:data waitUntilDone:YES];
});
[self.tableView reloadData];
//method called in viewdidload to create array...
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary* json = [NSJSONSerialization JSONObjectWithData:responseData //1
options:kNilOptions
error:&error];
NSLog(#" %#",json);
NSArray* latestItems = [json objectForKey:#"items"];
NSLog(#" array:%#",latestItems);
//getItems is a property in .h file
self.getItems = latestItems;
NSLog(#"getItems %#",_getItems); //logs out array ok
int size = [_getItems count];
NSLog(#"there are %d objects in the array", size);//provides correct number
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"getItems %#",_getItems); //logs (null)
int size = [_getItems count];
NSLog(#" %d objects in the array", size);//logs 0
When I count rows in viewdidload after creating the array, I get the correct number, however, when I call count on the array in the delegate method, it returns zero possibly because the tableview is created before Viewdidload is called.
Where should I create the array so that is known by the time numberofrows counts the number of rows in the array?
Edit:
After constructing the array, I save it to a property. However, I have discovered that this property is empty when I then log it to console in the numberofrowsinsection method so the problem seems to lie in how I am storing this array.
Right now, I have a property in the .h file and I've also tried it in the implementation but either way it is not persisting for some reason.
I'm not to familiar with obj-C, but I know you need to initialize your array outside your viewDidLoad() function. The reason why your .count is returning zero, is because your array is acting as a local variable to your viewDidLoad() function. Instead you could initialize the array as field in your UITableViewController class. This is how you would do so in swift, but it applies to obj-C as well:
class YourTableViewController: UITableViewController {
var yourArray = [AnyObject]()
override func viewDidLoad() {
//You can still do any programming to set up values and elements in yourArray[] here
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return yourArray.count
}
//Plus all your other tableView functions...
}
Also if you are passing information to your array between other UIViewController's you can add this function to your class, so every time you come back to your table view it loads the correct table cell count:
override func viewWillAppear(animated: Bool) {
self.tableView.reloadData()
}
I suggest to load the content of your array in viewDidLoad(), that is called once and before the table view use the array. The table view do not load the items before viewDidLoad. Are you doing something much different than this example structure below?
#implementation ViewController {
NSArray *arrayList;
}
- (void)viewDidLoad {
[super viewDidLoad];
arrayList = #[#"item 1", #"item 2"];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
cell.textLabel.text = [arrayList objectAtIndex:indexPath.row];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return arrayList.count;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
Consider abstracting the creation of your data - the array instantiated in your tableview, into a model instead. This is generally considered to be a better software engineering practice (read : https://developer.apple.com/library/ios/documentation/General/Conceptual/DevPedia-CocoaCore/MVC.html ).
What I would do is have another file as a Class or Struct, and populate the data for the array used in your table view in there. I would also recommend having setter/getter methods in your array class/struct as well. When loading the tableview, we would then grab the data from the class/struct in your tableview viewDidLoad() method. The data from your array would also be available at any other point of your application as well, as it is no longer dependant on the tableview.
Side note : You can consider making the class/struct a singleton as well, if the model is supposed to only get instantiated once.

UITableView with Multiple Data Arrays

I have an app, in which I have 7 different UITableViewControllers. All 7 are linked through a tabBarController. I am looking for a way to have a single custom class to be used throughout all 7 UITableViewControllers. I have 7 different arrays that all hold a specific number of objects. I need to know how to:
Change the number of rows in the tableView, depending on the array that I'm using as my data source.
Change the array that is being used as the data source based on which ViewController the user is currently looking at (Can this even be done?)
Change the contents of a cell, based on the array being used as the data source.
I'm familiar with using UITableView with a single data source, but I really don't know how to approach it with multiple data sources.
Thanks!
You can have one class be the dataSource for all of the UITableViewControllers
You might implement this by creating a custom subclass of UITabBarController which keeps an array of UITableViewControllers and a corresponding dictionary that maps a UITableVC to the array used by it's data source.
Set that as the data source for all the UITableViews and then handle each dataSource method like my example below.
Take a look at the UITableViewDataSource docs.
All of the methods pass in which tableView they're trying to get information about.
For example:
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//Here you could compare the tableView var to your array of tableViews to figure out which table view called this
//Based on that you could query your dictionary to find the array that houses the data for that tableView.
//Use the indexPath to find the data that you need to create and return the right cell
}
• Change the number of rows in the tableView, depending on the array
that I'm using as my data source.
You can accomplish this by conditions in tableView delegates
- (NSInteger)tableView:tableView numberOfRowsInSection:section
Inside this delegate you need to identify which dataSource for the particular tableView.
Check the table if its the one being refreshed like so:
- (NSInteger)tableView:tableView numberOfRowsInSection:section
{
if (tableView == self.firstTableView)
return self.firstTableDataSource.count;
if (tableView == self.secondTableView)
return self.secondTableDataSource.count;
//and so on..
}
• Change the array that is being used as the data source based on
which ViewController the user is currently looking at (Can this even
be done?)
Figuring which array you will be using for that particular table is up to you. You can use segement control, buttons, another table, it's up to you.
But the very important part is [tableView reloadData]; at your target table (table that is currently active) and again table delegates will be triggered and you will be doing all the filtering inside those delegates..
while you can check if the viewController is visible by:
if ([self/*viewController*/ isViewLoaded] && self/*viewController*/.view.window)
{
//visible
}
which was already discussed here
• Change the contents of a cell, based on the array being used as the
data source.
This one is not clear.
Is it just the content/values of the subviews of the cell like: cell.textLabel, cell.detailTextLabel and cell.imageView?
or the cell.contentView which is basically, you want to change the look of your cell?
If content/values again you just have to determine which is which, like this (using customCell):
assuming you have a dataSource that looks like:
{
data_source = (
{
text_label = test0;
detail_label = "this is just a text";
image_name = "your_image0.png";
},
{
text_label = test1;
detail_label = "this is just a another text";
image_name = "your_image1.png";
}
)
}
then in the delegate cellForRowAtIndexPath it'll be something like:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellID = #"tableID";
self.customCell = [tableView dequeueReusableCellWithIdentifier:cellID];
if (!self.customCell)
self.customCell = [[YourCustomCell alloc] initWithStyle:(UITableViewCellStyle) reuseIdentifier:cellID];
static NSString *dataSource = #"data_source";
static NSString *textLabel = #"text_label";
static NSString *detailLabel = #"detail_label";
static NSString *imageName = #"image_name";
if (tableView == self.firstTableView)
{
self.customCell.textLabel.text = [self.firstDataSource valueForKey:dataSource][indexPath.row][textLabel];
self.customCell.detailTextLabel.text = [self.firstDataSource valueForKey:dataSource][indexPath.row][detailLabel];
self.customCell.imageView.image = [UIImage imageNamed:[self.firstDataSource valueForKey:dataSource][indexPath.row][imageName]];
}
if (tableView == self.secondTableView)
{
self.customCell.textLabel.text = [self.secondDataSource valueForKey:dataSource][indexPath.row][textLabel];
self.customCell.detailTextLabel.text = [self.secondDataSource valueForKey:dataSource][indexPath.row][detailLabel];
self.customCell.imageView.image = [UIImage imageNamed:[self.secondDataSource valueForKey:dataSource][indexPath.row][imageName]];
}
// and so on...
}
To check all other methods, check apples documentation ,i hope this is useful for you and for others as well.. Happy coding.. :)

How Can I Change Tableview Array with Segment Control

I Have a one table view and i have two array. My arrays name AllItems and SpecialItems. I Use segment control. I wantto if segment value is 0 tableview load AllItems Array, When change segment value and value is = 1 than mytableview reload tada but SpecialItems array. Can u help me please. Thanks.
I solved this problem with table tag.
- (IBAction)segmentControlChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 1) {
mytable.tag = 1;
}
else
{
mytable.tag = 0;
}
[mytable reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
if(tableView.tag==1)
{
return [specialItems count];
}
else
return [allItems count];
}
You could create two data source classes that implement all the UITableViewDataSource methods: one for AllItems and one for SpecialItems. To switch between the two, connect a valueChanged action. In the method that is called, set the data source and reload the table view.
- (void)valueChange:(UISegmentedControl *)sender
{
if (/* condition for all items */) {
self.tableView.dataSource = self.allItemsDataSource;
} else {
self.tableView.dataSource = self.specialItemsDataSource;
}
[self.tableView reloadData];
}
I would personally create an array which the data is loaded from. Put this in your implementation:
NSArray * _tableData
Then in your viewDidLoad just allocate this for the array which we want it to start on.
_tableData = [[NSArray alloc] initWithArray:allItems];
This initially loads the data we will always see as the segment control starts on index 0. We have to set the initial data somewhere so the tableView loads with some data in it.
Then set the number of rows and the cellForRowAtIndex to pick up from the _tableData array
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return _tableData.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView_ cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell * cell = [tableView_ dequeueReusableCellWithIdentifier:bCell];
// Here we use the specific array as we would normally
return cell;
}
This step means the tableView will load with the array. Even if the array is empty the view will still load as the number of cells will be zero.
Now in our value changed function we can reset the array as we need to:
- (IBAction)segmentControlChanged:(UISegmentedControl *)sender {
if (sender.selectedSegmentIndex == 1) {
_tableData = allItems;
}
else {
_tableData = specialItems;
}
[self.tableView reloadData];
}
You just need to make sure the segment control changed is linked up in the XIB file (or programatically) and that you reload the table after choosing the array.
This kind of thing is actually really easy to do. I would definitely recommend working it through step by step if you're having trouble. Make sure each step is working before applying the next:
Get the tableView loading with both sets of data individually
Confirm that the segment control is calling the change function when clicked
Then that should do it

Coredata and UITableViewController row re-order

I have a UITableViewController that is displaying rows from my NSFetchedResultsController. My has several sections, with the rows being ordered within each section, using an 'order' field.
I'm using fairly bog-standard code to handle the moving of rows. The rows moves are restricted to the section they are contained within. When a row rearrange takes place, I use the following code..
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)sourceIndexPath toIndexPath:(NSIndexPath *)destinationIndexPath
{
self.fetchedResultsController.delegate = nil;
NSMutableArray *myImages = [[self.fetchedResultsController fetchedObjects] mutableCopy];
// Grab the item we're moving.
NSManagedObject *myImage = [[self fetchedResultsController] objectAtIndexPath:sourceIndexPath];
// Remove the object we're moving from the array.
[myImages removeObject:myImage];
// Now re-insert it at the destination.....here lies the problem???
[myImages insertObject:myImage atIndex [destinationIndexPath row]];
// All of the objects are now in their correct order. Update each
// object's displayOrder field by iterating through the array.
int i = 0;
for (MyImage *myImage in myImages)
{
myImage.order = [NSNumber numberWithInt:i];
i++;
}
// Save the data here!
self.fetchedResultsController.delegate = self;
}
(Some variable names have been changed to protect the innocent!!!)
This works quite well but doesn't always perform as expected. The reason being (I think) is I'm removing the object from the array successfully, but insertion again is wrong because it uses the absolute row index of the whole array, without allowing for the fact that this may be in (say) the second or third section of the UITableView/NSFetchedResultsController.
So, how can I put an object back into my mutable array (which is linear) into my the correct index, given the destination indexPath is two-dimensional? (I could probably count previous rows/section) but wonder if there's a prettier way.)
Thanks

Resources