tableview indexing confusion - ios

I am creating simple reveal menu in my demo application. Right now what I am doing is I have a one MutableArray named MutableMenu
NSArray menu = [#"menu1",#"Menu2","Menu3",#"Menu4"];
MutableMenu = [NSMutableArray alloc]initwithArray:menu];
I am just displaying this mutable array in my by default tableview cell.
In cellForRowAtIndexPath method I am using static indexing like below code snippet
if(indexPath.row == 0){
[self performSegueWithIdentifier:SEGUE_TO_MANAGE_TASK sender:nil];
}
if(indexPath.row == 1){
[self performSegueWithIdentifier:SEGUE_TO_MANAGE_LEAVE sender:nil];
}
All is working great till now. Now I have a array that I want to check permission to display in tableview cell. For example first user have only 2 permissions from 4 menus. This user only able to access menu1 and menu2. Likewise different users different permissions.
This is my code to maintaining this scenario in viewDidLoad. All is working fine
BOOL permisson = NO;
NSArray *Roles = [USERDEFAULT objectForKey:PREF_ROLE_ARRAY];
for (NSString *temp in Roles) {
if ([temp isEqualToString:#"55"] || [temp isEqualToString:#"45"] || [temp isEqualToString:#"67"] ) {
permisson = YES;
if ([Mutablemenu containsObject:#"Team management"])
NSLog(#"Already Have");
else [Mutablemenu insertObject:#"Team management" atIndex:2];
}
}
if (!permisson) {
[Mutablemenu removeObject:#"Team management"];
[Mutablemenu insertObject:#"" atIndex:2];
[MutableImages removeObject:#"team.png"];
}
[_tableview reloadData];
All is working fine But when user have only 3 permission.My indexing is changed in didSelectRowatIndexPath. For Example first user have 4 permission then index is 0,1,2,3 but when user have 3 permission then index is 0,1,2.
But my question is how do I handle this situation in didSelectRowatIndexPath. Because every time indexing is changed.

As told by #RJV Kumar, you should have check actual menu clicked instead of indexPath in didSelectRowatIndexPath as:
if([menuArray[indexPath.row] isEqualToString: #"Menu1"]){
[self performSegueWithIdentifier:SEGUE_TO_HANDLE_MENU_1 sender:nil];
}
else if([menuArray[indexPath.row] isEqualToString: #"Menu2"]){
[self performSegueWithIdentifier:SEGUE_TO_HANDLE_MENU_2 sender:nil];
}
.
.
so on...

As per my understanding, you are finding the menu based on index, but you should not do like that,
instead of that maintain menuid or menuname to identify which menu the user selected. By this way, you don't have to worry about the index.
In didselectRow method, by comparing menuid or menuname, do your operation

Related

Stop scrolling on UITableView at last during XCUI TestCase

In my one of UITableView have more then 10 rows. I want to scroll till last row while UITestCase running.
I have written below code to scroll till last row.
-(void)scrollToElement:(XCUIElement *)element application:(XCUIApplication *)app{
while ([self visible:element withApplication:app]) {
XCUIElement *searchResultTableView = app.tables[#"searchResultView"];
XCUICoordinate *startCoord = [searchResultTableView coordinateWithNormalizedOffset:CGVectorMake(0.5, 0.5)];
XCUICoordinate *endCoord = [startCoord coordinateWithOffset:CGVectorMake(0.0, -262)];
[startCoord pressForDuration:0.01 thenDragToCoordinate:endCoord];
}
}
-(BOOL)visible:(XCUIElement *)element withApplication:(XCUIApplication *)app{
if (element.exists && !CGRectIsEmpty(element.frame) && element.isHittable) {
return CGRectContainsRect([app.windows elementBoundByIndex:0].frame, element.frame);
} else {
return FALSE;
}
}
An i have called above method in my one of UITestCase method by below code
XCUIElement *searchResultTableView = app.tables[#"searchResultView"];
[self waitForElementToAppear:searchResultTableView withTimeout:30];
XCUIElement *table = [app.tables elementBoundByIndex:0];
XCUIElement *lastCell = [table.cells elementBoundByIndex:table.cells.count - 1];
[self scrollToElement:lastCell application:app];
By this code i can scroll to last row but after reaching last row, it continue doing scroll means can't stop scrolling.
Please help me to scroll to only last row and then it should stop to scroll so that i can perform next action event.
I have refer StackOverFlow answer but none of them meet my requirement.
Thanks in advance.
I faced similar issue in one of my project.
In that I wanted to test "Load More" feature by TestKit framework.
Here is some workaround to achieve the same scenario.
//app : is your current instance of appliaction
//listTable : is a Table which you've found via accessibility identifier
//loadMoreTest : is a parameter to determine whether code should perform test for loadmore feature or not
- (void)testScrollableTableForApplication:(XCUIApplication *)app
forTable:(XCUIElement *)listTable
withLoadMoreTest:(BOOL)loadMoreTest {
[listTable accessibilityScroll:UIAccessibilityScrollDirectionUp];
[listTable swipeUp];
if (loadMoreTest) {
__block BOOL isLoadMoreCalled;
__block XCUIElement *lastCell;
__block __weak void (^load_more)();
void (^loadMoreCall)();
load_more = loadMoreCall = ^() {
XCUIElementQuery *tablesQuery = app.tables;
XCUIElementQuery *cellQuery = [tablesQuery.cells containingType:XCUIElementTypeCell identifier:#"LoadMoreCell"];
lastCell = cellQuery.element;
if ([lastCell elementIsWithinWindowForApplication:app]) {
[self waitForElementToAppear:lastCell withTimeout:2];
[lastCell tap];
isLoadMoreCalled = true;
[self wait:2];
}
[listTable swipeUp];
if (!isLoadMoreCalled) {
load_more();
}
};
loadMoreCall();
}
}
- (void)waitForElementToAppear:(XCUIElement *)element withTimeout:(NSTimeInterval)timeout
{
NSUInteger line = __LINE__;
NSString *file = [NSString stringWithUTF8String:__FILE__];
NSPredicate *existsPredicate = [NSPredicate predicateWithFormat:#"exists == 1"];
[self expectationForPredicate:existsPredicate evaluatedWithObject:element handler:nil];
[self waitForExpectationsWithTimeout:timeout handler:^(NSError * _Nullable error) {
if (error != nil) {
NSString *message = [NSString stringWithFormat:#"Failed to find %# after %f seconds",element,timeout];
[self recordFailureWithDescription:message inFile:file atLine:line expected:YES];
}
}];
}
create one category for XCUIElement
XCUIElement+Helper.m and import it into your respective Test class.
#import <XCTest/XCTest.h>
#interface XCUIElement (Helper)
/// Check whether current XCUIElement is within current window or not
- (BOOL)elementIsWithinWindowForApplication:(XCUIApplication *)app ;
#end
#implementation XCUIElement (Helper)
/// Check whether current XCUIElement is within current window or not
/*
#description: we need to check particular element's frame and window's frame is intersecting or not, to get perfectly outcome whether element is currently visible on screen or not, because if element has not yet appeared on screen then also the flag frame, exists and hittable can become true
*/
- (BOOL)elementIsWithinWindowForApplication:(XCUIApplication *)app {
if (self.exists && !CGRectIsEmpty(self.frame) && self.hittable)
return CGRectContainsRect(app.windows.allElementsBoundByIndex[0].frame, self.frame);
else
return false;
}
#end
To get the "Load More" cell, i've given the
cell.accessibilityIdentifier = #"LoadMoreCell";
Rest of the code is, recursive function in testScrollableTableForApplication to make Tableview scroll to reach to bottom so i can have the access of load more cell(in your case last cell). Then i am performing the action Tap to fetch new records from server. Then again i am scrolling the Table to verify if the new records has been fetched from server or not.
Tip : you can replace recursive function with do while or while loop to achieve the same.
Hope this helps!
Happy Coding!!

iOS Update Reload Contents Timer

In my VC I have a UITableView. Each cell has a UITableView as one of its contents. Timer is set updating each cell every 10secs. Events are handled which also reloads the respective cell.
Method that timer calls :-
-(void) updateVisitotsLists {
NSLog(#"UPDATING VISITORS LIST ***************************");
// Call API's to get lists
[api getVisitorsList];
// Init Arrays
browsingList = [MainAppDataObject sharedAppDataObject].visitors_browsingList;
secondList = [MainAppDataObject sharedAppDataObject].visitors_secondList;
thirdList = [MainAppDataObject sharedAppDataObject].visitors_thirdList;
fourthList = [MainAppDataObject sharedAppDataObject].visitors_fourthList;
// AS these are no more useful, so make it nil & save memory
[MainAppDataObject sharedAppDataObject].visitors_browsingList = nil;
[MainAppDataObject sharedAppDataObject].visitors_secondList = nil;
[MainAppDataObject sharedAppDataObject].visitors_thirdList = nil;
[MainAppDataObject sharedAppDataObject].visitors_fourthList = nil;
// Reload all lists with latest data
[self reloadBrowsingRow];
[self reloadSecondRow];
[self reloadThirdRow];
[self reloadFourthRow];
}
Event Handler Method :-
-(void) handleNewVisitor : (NSNotification *) notification {
// UPDATE VISITOR'S LIST
Visitor *v = [notification object];
#try {
if (v != nil) {
// Add V to browsing list
[browsingList addObject:v];
// Reload browsing list
[self reloadBrowsingRow];
}
}#catch (NSException *e) {
NSLog(#"EXCEP - %#", e);
}
v = nil;
return;
}
Reloading Method -
-(void)reloadBrowsingRow {
// Browsing
VisitorsListsCell *bcell = (VisitorsListsCell*)[self.visitorlistsTv cellForRowAtIndexPath:[NSIndexPath indexPathForRow:2 inSection:0]];
[bcell.listsTableView reloadData];
[bcell updateButtonText];
[bcell setNeedsDisplay];
bcell = nil;
return;
}
The Problem :-
When updateVisitotsLists is called thru timer, the updated contents are not reflected on cell.
When event handler method calls the same [self reloadBrowsingRow]; method, the contents of the cell are updated and reflected.
Due to this despite cells contents are updated but are not reflected until the state of cell is changed - expanded or collapsed.
I tried removing timer and cell updates properly on event caught, but when timer was on and event was caught, method is called but contents are not reflected on the screen.
I feel both methods may be calling reload method at same time, hence this must be happening or what ? How can this be handled making sure that the contents of cells are updated in any respect ? Any help is highly appreciated. Thanks.
Use [tableview reloadData]; on that method.
Because Reload on tableView will reload all data, so it will be more helpful.

How can I alternate view controller and not lose data

I have two UIViewControllers, one is a UIPickerViewController, the Other a UITableViewController. Ideally the Picker should get a request from the user to add x amount of some item to the tableView. The Picker gets user inputs and assigns them to variables val1, val2, val3, where val1 is the number of items (number of rows) and val2 is the name or label for the item.
PickerViewController.m
- (IBAction)add:(id)sender
{
TableViewController *tvc = [[TableViewController alloc] init];
[tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:tvc animated:YES completion:nil];
}
TableViewController.m
-(void)setValues:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
self.val1 = newVal1;
self.val2 = newVal2;
self.val3 = newVal3;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"UITableViewCell"];
// This is just a header which holds my "Add" button
UIView *header = self.headerView;
[self.tableView setTableHeaderView:header];
[self addNew:self.val1 :self.val2 :self.val3];
}
- (void)addNew:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
if(!self.numberOfRows){
NSLog(#"Initially no of rows = %d", self.numberOfRows);
self.numberOfRows = [self.val1 intValue];
NSLog(#"Then no of rows = %d", self.numberOfRows);
}
else
{
self.numberOfRows = self.numberOfRows + [newVal1 intValue];
NSLog(#"New no rows = %d", self.numberOfRows);
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.numberOfRows inSection:0];
// Only run when called again .. not initially
if(self.run != 0){
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationBottom];
self.run ++;
[self.tableView endUpdates];
}
}
// "ADD" button which should go back to the picker and get new items to add to the table
- (IBAction)testAdd:(id)sender
{
PickerViewController *pvc = [[PickerViewController alloc] init];
[self presentViewController:pvc animated:YES completion:nil];
}
Now, I realize every time I call the next view controller I am creating a new instance of it, but I don't know how else to do it, I figure this is the main problem. As of right now, I expect when I leave the tableview for the picker view and return the console should log "New no of rows = x" but that doesn't happen.
I know val3 isn't used and my addNew: may not be the best, but I just need it to handle the basic logging mentioned above and I should be able to take it from there.
Been stuck on this for days
Create a property for TableViewController, and only create it the first time you present it,
- (IBAction)add:(id)sender {
if (! self.tvc) {
self.tvc = [[TableViewController alloc] init];
}
[self.tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:self.tvc animated:YES completion:nil];
}
It's not entirely clear from you question, whether it's this presentation or the one you have in the table view class that you're talking about. It also looks like you're doing something wrong in terms of presentation -- you're presenting the picker view from the table view controller, and also presenting the table view controller from the picker. That's not correct, you should present which ever controller you want to appear second, and that controller should use dismissViewControllerAnimated to go back, not present another controller.
In testAdd you don't need to create a new instance and present it. If you want to go back to the presentingViewController, just use dismissViewControllerAnimated .
And you will go one controller up in the stack.

Saving data from tableview cells in core data

Hi I am trying to save data into core data and having some trouble... I have a Team Entity and a Player Entity The Team Entity is set up as a one to many relationship with the Player Entity... on my "NewTeamViewController" there are two sections, in the second section is where you add players to the team. In the section header there is a button to add a new player... When that button is pressed a new cell appears with three textFields, each with default text in them (not placeholder text) then I am adding the new player to a MutableSet that will be added as the teams players. The tableview is using a custom cell (which is where the three textFields live)
The team saves correctly, except I am unable to save the data from the three text fields in the player cell. It just saves the default text in the cell for the player.
I am not sure how or where to give the data from the newly added cell to the newly added player object.
Here is some code...
-(void)saveButtonWasPressed {
self.team =[NSEntityDescription insertNewObjectForEntityForName:#"Team" inManagedObjectContext:self.managedObjectContext];
team.schoolName = _schoolName.text;
team.teamName = _teamName.text;
team.season = _season.text;
team.headCoach = _headCoach.text;
team.astCoach = _assistantCoach.text;
player.firstName = cell.playerFirstName.text;
player.lastName = cell.playerLastName.text;
player.number = cell.playerNumber.text;
[self.team addPlayers:_tempSet];
[self.managedObjectContext save:nil];
[self.navigationController popViewControllerAnimated:YES];
}
//////////////////////////////////////////////////////////////////////////////////////////////////
-(void)addPlayerButton {
player = (Player *)[NSEntityDescription insertNewObjectForEntityForName:#"Player"
inManagedObjectContext:self.managedObjectContext];
[_tempSet addObject:player];
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:1] withRowAnimation:UITableViewRowAnimationFade];
}
Add your NewTeamViewController as a target for the control event UIControlEventEditingChanged for each of your text fields. You can do this in code (cellForRowAtIndexPath...) or in your nib or storyboard. I would use a different action method for each of the three different text fields in each cell. Here is what your action methods might look like:
// Helper method
- (Player *)playerForTextField:(UITextField *)textField
{
NSIndexPath *indexPath = [self.tableView indexPathForCell:textField.superview];
return [_tempArray objectAtIndex:indexPath.row];
}
- (IBAction)firstNameDidChange:(UITextField *)textField
{
Player *player = [self playerForTextField:textField];
player.firstName = textField.text;
}
- (IBAction)lastNameDidChange:(UITextField *)textField
{
Player *player = [self playerForTextField:textField];
player.lastName = textField.text;
}
- (IBAction)numberDidChange:(UITextField *)textField
{
Player *player = [self playerForTextField:textField];
player.number = textField.text;
}
Also, change your _tempSet to a _tempArray because knowing the order of the players in the table is useful.

How to update a UITableView in detailview when changes the tabbar in the pop over controller?

I am creating an iPad app with splitview, here is the screen shot,
In this one, I want to update the values in the righthand side tableview, when I change the tab in the masterview controller (left hand side). Which will be the good aproach, Should I load another viewcontroller for each tab change? Is it possible? Or just update the table? For all the tab changes i want to display a tableview with different data.
I used following code, i can see the changes in the log, but the table is not getting updated.
- (void)setDetailItem:(id)newDetailItem
{
if (_detailItem != newDetailItem) {
[_detailItem release];
_detailItem = [newDetailItem retain];
// Update the view.
[self configureView];
}
if (self.masterPopoverController != nil) {
[self.masterPopoverController dismissPopoverAnimated:YES];
}
}
- (void)configureView
{
// Update the user interface for the detail item.
if (isStudent) {
textStr = #"student";
NSLog(#"Student....%#",textStr);
[self.tableView reloadData];
}if (isTeachers) {
textStr = #"teacher";
NSLog(#"Teacher....%#",textStr);
[self.tableView reloadData];
}if (isPreference) {
textStr = #"preference";
NSLog(#"Preference....%#",textStr);
[self.tableView reloadData];
}if (isConfiguration) {
textStr = #"configuration";
NSLog(#"Configuration....%#",textStr);
[self.tableView reloadData];
}
}
I am also tried
[self performSelectorOnMainThread:#selector(refreshTableView) withObject:nil waitUntilDone:NO];
Please share your ideas.
thanks :)
At last I found the issue myself, and I was really great to search for a solution. I have went through several forums and tutorials. Finally I figured out the issue. And in this video, they have illustrated how to create a SplitView application using XCode 4.2. In this one, just one line of code fixed the issue. That is in the Appdelegate.m file. In default, the MasterViewController doesn't have access to the detail view, so if we need to do something on the detailview, we have to connect masterviewcontroller and detailviewcontroller. Check the video, then you will (those who are facing the same issue) understand.
Thanks :)

Resources