Note that there are some explanatory texts on larger screens.

plurals
  1. PORepeated calls to setRightBarButtonItems on UINavigationItem consume more and more time in each cycle
    primarykey
    data
    text
    <p>My question seems to need a real experienced iOS developer's advice.</p> <p>In my app I've implemented a <strong>Container View Controller</strong> (class: CC_CosyLogContainerVC), which consists of 3 subviews:</p> <ul> <li>a header subview with Buttons to activate 1 of <strong>3 child view controllers</strong>,</li> <li>a middle subview, which displays some data and</li> <li>a bottom subview, which is the placeholder for the child view controllers.</li> </ul> <p>The container view controller is embedded in a UINavigationController, which also gets accessed by it's child view controllers, when they become active. The child view controllers set the navigationController's navigationItem.title and navigationItem.rightBarButtonItems according to their needs.</p> <p>This whole management of activating and deactivating the child view controllers runs very well. There are no memory leaks.</p> <p>But I've detected a <strong>performance issue</strong>, which I further examined with Instruments. That is, why I am able now to understand the problem and to describe it here. Unfortunately I have no idea how to solve it!</p> <p>At the beginning, when the container view controller did load, the child view controllers also display their views very fast. When I tap a button in the container's header subview to switch to another child view controller, this happens really fast - but only at the beginning. I discovered, that each time I do such a child switch, this takes more and more time.</p> <p>Using Instruments I've found, that the "Button dance" in the NavigationBar is responsible for this time consumption. Each time, when one of the child view controller's main view appears, they call a method to configure the available Buttons for the NavigationBar.</p> <p>As a typical example, I show the code for "addCameraButton":</p> <pre><code>- (void)addCameraButton { if ((self.cosyLogID) &amp;&amp; ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera])) { if (!self.cameraButtonAdded) { //add new Camera Button UINavigationItem *myNavigationItem = (self.parentViewController ? self.parentViewController.navigationItem : self.navigationItem); NSMutableArray *newArray = [[NSMutableArray alloc] initWithArray:myNavigationItem.rightBarButtonItems]; [newArray addObject:self.cameraButton]; myNavigationItem.rightBarButtonItems = newArray; self.cameraButtonAdded = YES; } } else { //remove old Camera Button if it already exists [self removeCameraButton]; } } </code></pre> <p>The performance critical line of code is</p> <pre><code> myNavigationItem.rightBarButtonItems = newArray; </code></pre> <p>Instruments time profiler shows, that this line consumes 99,9% of the whole method. On an iPhone4, [addCameraButton] needs about <strong>50 ms</strong> of time, when it is called the first time. The second call (when I switched to another child and then switch back to this one) takes about <strong>75 ms</strong> of time.</p> <p>By extensive testing and manual switching between the childs, the needed time to set the rightBarButtonItems array grows and grows <strong>up to 500 ms and more</strong> - there seems to be no limit.</p> <p>This leads to a bad responsiveness, as the app gets slower and slower. The appearing of the childs' views takes more and more time because of this navigation buttons issue.</p> <p>How can advise and has an idea, how I can resolve this <strong>growing time consumption issue when setting the rightBarButtonItems</strong>?</p> <hr> <p><strong>Additional information:</strong></p> <p><strong>Method [removeCameraButton]</strong>, which is the counterpart for [addCameraButton] and called in child's [viewWillDisappear]:</p> <pre><code>- (void)removeCameraButton { UINavigationItem *myNavigationItem = (self.parentViewController ? self.parentViewController.navigationItem : self.navigationItem); NSArray *oldArray = myNavigationItem.rightBarButtonItems; NSMutableArray *newArray = [[NSMutableArray alloc] init]; BOOL doAdd; for (id myObject in oldArray) { doAdd = YES; if ([myObject isKindOfClass:[UIBarButtonItem class]]) { if ([(UIView *)myObject tag] == CC_TAG_FOR_LOGFOTOSVIEW_BTN_CAMERA) { doAdd = NO; } } if (doAdd) { [newArray addObject:myObject]; } } myNavigationItem.rightBarButtonItems = newArray; self.cameraButtonAdded = NO; } </code></pre> <p><strong>Observation of the rightBarButtonItems array</strong>: I did check [newArray count] in the debugger. It does not grow over the cycles, it definitely always contains the right number and kind of UIBarButtonItems.</p> <p>The <strong>Getter method for the camera button</strong>:</p> <pre><code>-(UIBarButtonItem *)cameraButton { if (!_cameraButton) { _cameraButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCamera target:self action:@selector(cameraButtonPressed:)]; _cameraButton.tag = CC_TAG_FOR_LOGFOTOSVIEW_BTN_CAMERA; } return _cameraButton; } </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload