Sorry if I missed anything about this but I have a table view with 2 large sections and an index to navigate between sections:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSMutableArray *listArray = [[NSMutableArray alloc] init];
listArray = [NSArray arrayWithArray:[#"S|H"componentsSeparatedByString:#"|"]];
return listArray;}
Since I only have 2 sections the top index "S" is at the top and "H" is at the extreme bottom of the screen.
Is there any way to reposition those two index letters to be located at the center? (or at least near each other)
Thanks
I don't think so - positioning the labels is really up to the OS, and finally it meets user expectation.
Your code leaks an NSMutableArray though, as you first alloc/init an array, and then reassign the variable to another array.
Also, arrayWithArray: is overkill here.
Make it
NSArray *listArray = [#"S|H"componentsSeparatedByString:#"|"];
or better yet
NSArray *listArray = [NSArray arrayWithObjects:#"S", #"H", nil];
Related
I have a UISegmentControl with my UITableView that sorts the data. I'd like to be able to do things:
(1) default sort (so when the user turns on the app for the first time, it would select the first segment, and sort by that action)
(2) remember where the user was between table loads. What I mean by this is, similar to Apple's coverflow, when I go to a different cover, the UITableView repopulates. So if the last time the user was there, the sort was on the 3rd segment, then it would remember that.
I'm a bit new to object-oriented design, and this was my best guess to not have the same redundant code everywhere: (MarkersList is a NSMutableArray)
- (NSArray *)sortByName:(NSArray *)sortDescriptors {
return [self.MarkersList sortedArrayUsingDescriptors:sortDescriptors];
}
- (NSArray *)sortByRSID:(NSArray *)sortDescriptors {
return [self.MarkersList sortedArrayUsingDescriptors:sortDescriptors];
}
- (void)setSortedMarkersList:(NSArray *)sortedArray {
if (self.MarkersList != nil) {
[self.MarkersList removeAllObjects];
}
[self.MarkersList addObjectsFromArray:sortedArray];
}
- (IBAction)sortButtonPressed:(UISegmentedControl *)segmentControl {
// Create sort descriptors
NSSortDescriptor *nameDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"Name" ascending:YES selector:#selector(localizedCaseInsensitiveCompare:)] autorelease];
NSSortDescriptor *rsID = [[NSSortDescriptor alloc] initWithKey:#"ID" ascending:YES];
if ([segmentControl selectedSegmentIndex] == NAME) { // this is #define 0
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:nameDescriptor, rsIDDescriptor, nil];
NSArray *sortedArray = [self sortByGene:sortDescriptors];
[self setSortedMarkersList:sortedArray];
[sortDescriptors release];
}
else if ([segmentControl selectedSegmentIndex] == RS_ID) {
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:rsIDDescriptor, resultDescriptor, nameDescriptor, nil];
NSArray *sortedArray = [self sortByRSID:sortDescriptors];
[sortDescriptors release];
[self setSortedMarkersList:sortedArray];
}
[self.MarkersTableView reloadData];
}
I haven't implemented the third sort yet since it's not just an NSString or NSNumber like the other two yet. So far, I think it works correctly. However, the problem I have is to implement (1), I would need to call sortByName when my table is loaded. I could just create the NSSortDescriptors again, but that seems redundant. Is there a more OOD way to achieve this?
For (2), I'm guessing I could save that index for the table in a dictionary, and retrieve it when that table is loaded. Or something along those lines, not really sure.
Any help is greatly appreciated. Thanks!
This isn't so much an OO question, as iOS doesn't always let us use best practices to solve a problem. The best bet for your situation, I think, is to store the information on the selected sort in the NSUserDefaults. In your -viewDidLoad method, check if the selected sort object exists in NSUserDefaults, use it if it does, and if not choose a reasonable default value.
Don't worry about creating NSSortDescriptors with each load of the application, unless you have done profiling and determined that a large amount of time is spent building it. Serializing and deserializing the NSSortDescriptors would be far more inefficient than just recreating it when needed. Apple spends a lot of time optimizing frequently used classes like NSSortDescriptor.
Regarding #2, you can use indexPathsOfVisibleRows on the UITableView to get an array of visible indices, store the first one in UserDefaults, then on load (or pop from the next view controller if that occurs) call –scrollToRowAtIndexPath:atScrollPosition:animated:.
Once you're code is functioning, I recommend you watch the various videos on iTunes U regarding profiling and Instruments. It's a wonderful tool that is often overlooked, and really helps concentrate effort where it's needed.
I am currently looking at Apple's AddMusic example and playing around with it before I start rewriting it into my application.
I noticed that it makes its own little playlist of the songs qued. I want to use the swipe action on the table view to remove songs that a use clicked by mistake.
I have implemented the swipe action but can't work out a way to delete that specific row?
Any idea would be great, below is the code to add it. I tried doing the reverse with no luck. If it's not possible how should I go about it?
Cheers
MainViewController *mainViewController = (MainViewController *) self.delegate;
MPMediaItemCollection *currentQueue = mainViewController.userMediaItemCollection;
MPMediaItem *anItem = (MPMediaItem *)[currentQueue.items objectAtIndex: row];
An MPMediaItemCollection is immutable, ie. you can't change the items. You need to create a new one with all items less the one you want to remove. See below:
NSArray* items = [currentQueue items];
NSMutableArray* array = [NSMutableArray arrayWithCapacity:[items count]];
[array addObjectsFromArray:items];
[array removeObjectAtIndex:row];
MPMediaItemCollection* newCollection = [MPMediaItemCollection collectionWithItems:array];
Be careful to not create an empty collection. It's not allowed and the MPMediaItemCollection will raise an exception.
I have a UIToolbar that I set up using IB with three buttons, left, middle and right. In some situations I would like to not display the middle button. Does anybody know of a way to hide a specific button on inside a UIToolBar? There is no hide property, all I can find is setEnable but this still leaves the button causing users to wonder what its purpose is. I would like to only display it in situations that it actually has a use.
Thanks in advance!
Reset the items:
-(void)setItems:(NSArray *)items animated:(BOOL)animated
You can get the current items using the items property, then just remove the one you don't want to show and pass in the new NSArray.
As you can see, you can also animate it to make it clear to the user.
Rather than guessing at the index, I added an IBOutlet for the UIBarButtonItem and then removed it by name:
NSMutableArray *toolBarButtons = [self._toolbar.items mutableCopy];
[toolBarButtons removeObject:self._selectButton]; // right button
[self._toolbar setItems:toolBarButtons];
And of course it helps to connect the outlets in the designer :)
This is how i did it.. too much headache but its the best i could come up with :
NSArray *toolBarArray = toolBar.items;
NSMutableArray *newToolBarArray = [NSMutableArray arrayWithArray:toolBarArray];
[newToolBarArray removeObjectAtIndex:2];
[newToolBarArray removeObjectAtIndex:1];
//remove whatever buttons you want to.
NSArray *finalTabBarArray =[[NSArray alloc] initWithObjects:newToolBarArray, nil];
[toolBar setItems:[finalTabBarArray objectAtIndex:0] animated:NO];
I know it is quite old thread for but those who look this page for solution, here you go :
With iOS7, you can use this approach to show/hide your toolbar button :
if(// your code Condition)
{ self.toolbarBtn1.enabled = YES;
self.toolbarBtn1.tintColor = nil; }
else
{ self.toolbarBtn1.enabled = NO;
self.toolbarBtn1.tintColor = [UIColor clearColor]; }
This does not work here because the array you are sending with setItem is not what the function expects.
I had to replace the line:
NSArray *finalTabBarArray = [[NSArray alloc] initWithObjects:newToolBarArray, nil];
with this one:
NSArray *finalTabBarArray = [newToolBarArray copy];
Then it works perfectly.
Mohit's answer is one that I have used, but you dont need to specifically make it a NSArray that the toolbar sets. You can just set the array of items as a NSMutableArray. No real advantage that I am aware off but its a few lines less code. And that way you can take the array and move about UIButton objects as you would any other array with objects and then just reset the toolbar with that mutable array.
[newToolBarArray removeObjectAtIndex:2];
[newToolBarArray removeObjectAtIndex:1];
[toolBar setItems:newToolBarArray];
So in my view controller, I run code to populate an NSArray of Customer (custom class) objects. This custom class has objects that are of ANOTHER custom class called Address (a customer has a billing address and a shipping address). In the view controller when a customer in the list is selected, it passes a new view controller a customer object, like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
InfoViewController *customerinfoViewController = [[InfoViewController alloc] initWithStyle:UITableViewStyleGrouped andCustomer:[[[customers objectAtIndex:indexPath.section] objectAtIndex:indexPath.row] retain]];
[self.navigationController pushViewController:customerinfoViewController animated:YES];
[customerinfoViewController release];
}
The first time I visit this view controller while running the application, it works fine. However, when I revisit the view controller, something interesting happens. The application crashes, with unrecognized selector sent to instance 0x00whatever. Using the mouseover debugging feature in xCode, I am finding that the first object of the customer's shipAddress variable has its type changed from NSString to NSIndexPath. This does not happen to the customer's billAddress object. Anyone have any idea what is going on here? It seems like I may be having memory management issues but I would definitely like a confirmation on this before I tear my code apart tracking down all the retains and releases....
EDIT: More information here. with the following code, I have an NSMutableArray at the class level. At each iteration of the loop, I am looping through nodes in XML (which works fine). Every time a new letter is detected as the first letter of the name, I create a new subarray and add the customer to it, thus filling my class-level NSMutableArray (customers) with subArrays of customers for each letter of the alphabet detected. My question is about the retains and releases of the cycling customer object. Clang Static says there is an over-retaining error on the customer, but when I fix it according to Clang, the loop crashes. what gives? Related code below:
DDXMLDocument *rootDoc = [[[DDXMLDocument alloc] initWithData:xmlData options:0 error:nil] autorelease];
NSArray *elems = [rootDoc nodesForXPath:#"QBXML/QBXMLMsgsRs/CustomerQueryRs/CustomerRet" error:nil];
DDXMLNode *node;
sectionTitles = [[[NSMutableArray alloc] initWithCapacity:1] retain]; // Letters for UITableView section titles
NSMutableArray *subArray;
NSString *lastchar = #"A";
NSString *testchar;
int indexCount = -1;
customers = [[[NSMutableArray alloc] initWithCapacity:[elems count]] retain];
Customer *newCust;
for (int i = 0; i < [elems count]; i++) {
node = [elems objectAtIndex:i];
newCust = [[Customer alloc] initWithCustomerRetNode:node];
testchar = [[newCust fullName] substringToIndex:1];
if (i == 0 || ![[testchar uppercaseString] isEqualToString:lastchar]) {
[sectionTitles addObject:testchar];
lastchar = testchar;
indexCount++;
subArray = [[NSMutableArray alloc] initWithCapacity:1];
[customers addObject:subArray];
[subArray release];
[[customers lastObject] addObject:[newCust retain]];
}
else {
[[customers lastObject] addObject:[newCust retain]];
}
[newCust release];
}
NOTE: this code works for the most part, but clang doesn't like it.
EDIT: Addresses in the Customer class are assigned like so (which now does not work after Clang fixes)
...
else if ([tempname isEqualToString:#"BillAddress"])
billAddress = [billAddress initWithAddressNode:tempnode];
else if ([tempname isEqualToString:#"ShipAddress"])
shipAddress = [shipAddress initWithAddressNode:tempnode];
...
It sounds like you are having a over release issue, so yes memory management, you might be overreleasing that array you are storing your objects in.Cant really tell from the snippet of code though. Youll have to go and look through the code and find the source. Also using Clang Static Analyzer might be of help to you.
I have a table view in which I'm using sectionIndexTitlesForTableView to display an index. However, when I scroll the table, the index scrolls with it. This also results in very slow refreshing of the table. Is there something obvious I could be doing wrong? I want the index to remain in place on the right while the table scrolls. This is the code I'm using for the index titles:
- (NSArray *)sectionIndexTitlesForTableView:(UITableView *)tableView {
NSMutableArray *tempArray = [[NSMutableArray alloc] init];
[tempArray addObject:#"A"];
[tempArray addObject:#"B"];
[tempArray addObject:#"C"];
[tempArray addObject:#"D"];
...
return tempArray;
}
You really should be creating the index list somewhere else (say, in your table controller's init or loadView methods) and retaining it as an instance variable for later use. Then in sectionIndexTitlesForTableView you only have to return that ivar. If it isn't a property with a retain attribute then make sure you retain it when created so it sticks around (and release it in dealloc).
An easy way to create it is:
self.alphabetIndex = [NSArray arrayWithArray:
[#"A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|#"
componentsSeparatedByString:#"|"]];
The actual letters would have to change depending on the language locale setting but this way it's a bit easier to localize.
You definitely don't want to be creating that temp array each time because it's going to get called a lot.
As far as the index scrolling away it may be related to your returning a new array each time. Try the above first and if it doesn't solve the problem then you may want to tweak the value for the table's sectionIndexMinimumDisplayRowCount property and see if it makes any difference.
I would avoid creating a new NSMutableArray and releasing it every time. Try creating those on viewDidLoad or the class constructor and just reference the pre-built array on sectionIndexTitesForTableView.
If you are not manipulating the array at all, you probably don't need the overhead of an NSMutableArray at all. Try switching it to a plain old NSArray by using the arrayWithObjects static autorelease constructor.
That should speed things up for you.
Make a static variable, it will be released on app exit.
static NSMutableArray* alphabet = nil;
+ (void)initialize {
if(self == [MyViewController class]){
NSUInteger const length = 'Z' - 'A' + 1;
alphabet = [[NSMutableArray alloc] initWithCapacity:length];
for(NSUInteger i=0; i<length; ++i){
unichar chr = 'A' + i;
[alphabet addObject:[NSString stringWithCharacters:&chr length:1]];
}
}
}