Titanium Community Questions & Answer Archive

We felt that 6+ years of knowledge should not die so this is the Titanium Community Questions & Answer Archive

Removing all child objects from a view

I am trying to remove all child views from a parent view (possibleTagViewArea) using the following code:

if( possibleTagViewArea.children ){
  while( possibleTagViewArea.children.length > 0 ) {
    Ti.API.info( 'Number of children: ' + possibleTagViewArea.children.length );
    possibleTagViewArea.remove( possibleTagViewArea.children[0] );
    Ti.API.info( 'Deleted child at 0' );
  }
}

This seems like it should work. I'm simply removing the child at the zero position until the number of children reaches zero. However, I'm getting errors that almost seem to indicate that there is no rhyme or reason to how the child objects are deleted:

[INFO] Number of children: 7
[INFO] Deleted child at 0
[INFO] Number of children: 6
[INFO] Deleted child at 0
[INFO] Number of children: 5
[INFO] Deleted child at 0
[INFO] Number of children: 5
[WARN] Exception in event callback. {
line = 113;
message = "*** -[NSMutableArray objectAtIndex:]: index 4 beyond bounds [0 .. 3]";
sourceId = 245055424;
sourceURL = "file://localhost/Users/me/myapp/Resources/tags.js";
}

Notice that the number of children is output as 5 twice in the debug messages. Running this same portion of code yields errors at a different number of children each time.

Any help is greatly appreciated.

— asked November 10th 2010 by Warren Harrison
  • child
  • children
  • remove
  • view
  • views
0 Comments

8 Answers

  • Accepted Answer

    You might find it easier to wrap the child views in a single view, and remove that from the parent. Have you tried doing that?

    — answered November 11th 2010 by Kevin Whinnery
    permalink
    6 Comments
    • They are already wrapped in such a containing view. I guess I was thinking there was no real need to remove and recreate the containing view though, just the children. Seems the simplest solution at this point though. Thanks for the tip Kevin.

      — commented November 11th 2010 by Warren Harrison
    • Tried simply removing the containing view and re-adding it. Of course, its contents still exist, so I have to actually re-instantiate it as a brand new view before re-adding it. Success!

      — commented November 11th 2010 by Warren Harrison
    • How do you reinstantiate the view? I'm trying to do this and keep getting the same view back.
      Cheers!

      — commented December 8th 2011 by Dooley P
    • Nice tip, however not still not an essential solution. Arrays are one of the most basic features in any language, and besides one year after this question, this is still the case even in iOS.

      — commented February 10th 2012 by Leo Acevedo
    • I too would like to know how to re-instantiate a view

      — commented March 14th 2012 by David Shepheard
    • This method is working very well for me. Thx for sharing :)

      Here is my code for re-instantiating the content container view every time:

      // The main scrollview
      
      var scrollview = Ti.UI.createScrollView();
      
      // Some content
      
      var rows = ['row 1','row 2','row 3'];
      
      function getcontents()
      {
          // The container for the scrollview contents
      
          var contentcontainer = Ti.UI.createView();
      
          // Create a bunch of views to put in the container
      
          for (var a in rows)
          {
              var row = Ti.UI.createView();
              contentcontainer.add(row);
              row = null;
          }
      
          // Add the content container to the scrollview
      
          scrollview.add(contentcontainer);
      
          // Dereference the local variable
      
          contentcontainer = null;
      }
      
      getcontents();
      
      // ... later in the code
      
      function cleanup()
      {
          var scrollview_children = scrollview.getChildren();
          for (var b in scrollview_children)
          {
              if (undefined!==scrollview_children[b])
              {
                  scrollview.remove(scrollview_children[b]);
              }
          }
      }
      
      cleanup();
      

      The reason I am setting row = null; and contentcontainer = null; is to dereference the local variable objects from their value objects so that the only reference remaining in memory is the reference created by the .add functions. As soon as you remove the contentcontainer from the scrollview the container and it's child objects will be garbage collected.

      Hope this helps someone :)

      — commented December 19th 2012 by Robin Stoker
  • I think that using the children method inside a remove method is the main problem so I don't mix them.

    Doing it in two steps seems to work for me. Just save first an array and remove all objects later:

    if (view && view.children != undefined)
    {    
        // Save childrens        
        var removeData = [];
        for (i = view.children.length; i > 0; i--){
            removeData.push(view.children[i - 1]);  
        };
    
        // Remove childrens
        for (i = 0; i < removeData.length; i++){
            view.remove(removeData[i]);
        }
        removeData = null;
    };
    
    — answered June 27th 2012 by Lluis Gerard Lopez
    permalink
    3 Comments
    • This works well

      — commented November 26th 2012 by Andrew Church
    • I tried al the solutions here (except for Alloy), this is the only one that works for me without giving errors or breaking eventlisteners. (Ti 3.2.0 and iOS6)

      — commented December 16th 2013 by Jasper Pegtel
    • Same as Mr. Pegtel here, this is the only one working for me with the 3.2.0 SDK on Android and iOS. Thank you!

      — commented February 13th 2014 by Marc Andrew Landles
  • try looping backwards, i think you will get better results

    if( possibleTagViewArea.children ){
      while( possibleTagViewArea.children.length!=0  ) {
        var len = possibleTagViewArea.children.length;
        Ti.API.info( 'Number of children: ' + len );
        possibleTagViewArea.remove( possibleTagViewArea.children[len -1 ] );
        Ti.API.info( 'Deleted child at ' + len -1 );
      }
    }
    
    — answered November 10th 2010 by Aaron Saunders
    permalink
    5 Comments
    • Thanks for your suggestion, but it yields the same results, I'm afraid. It's as if the next delete is occurring before the length is updated, and so a non-existent index is trying to be deleted. Puzzling.

      — commented November 11th 2010 by Warren Harrison
    • hmm maybe try setting it to null will force the parent to release it for you. I am curious, will have to poke around the code

      — commented November 11th 2010 by Aaron Saunders
    • Try looping backwards without a moving target. The children.length above shrinks during the loop, so you never remove all of your children.

      var viewCount = view.children.length;
              for (i = 0; i < viewCount; i++) {
                  view.remove(view.children[((viewCount - 1) -i)]);
              }
      

      — commented December 9th 2011 by Jared Alessandroni
    • Jared Alessandroni, thanks a lot!!! It work very well!

      — commented March 2nd 2012 by Vittorio Sorbera
    • I tried ur answer

      if( possibleTagViewArea.children ){
      while( possibleTagViewArea.children.length!=0 ) {
      var len = possibleTagViewArea.children.length;
      Ti.API.info( 'Number of children: ' + len );
      possibleTagViewArea.remove( possibleTagViewArea.children[len -1 ] );
      Ti.API.info( 'Deleted child at ' + len -1 );
      }
      }


      Invalid type passed to function. expected: TiViewProxy, was: NSNull in -[TiViewProxy remove:] (TiViewProxy.m:129)
      [WARN] Exception in event callback. {
      line = 426;
      message = "Invalid type passed to function. expected: TiViewProxy, was: NSNull in -[TiViewProxy remove:] (TiViewProxy.m:129)";
      sourceId = 261218168;
      sourceURL = "file://localhost/Users/catalists/Library/Application%20Support/iPhone%20Simulator/5.0/Applications/A480EE33-B56E-4E48-9D9F-1730BEA73F76/YellowPages.app/ui/home.js";
      }

      — commented April 5th 2012 by Umaid Saleem
  • Since Ti SDK 3.1, you can call yourView.removeAllChildren( )

    See: http://docs.appcelerator.com/titanium/3.0/#!/api/Titanium.UI.View-method-removeAllChildren

    — answered December 16th 2013 by Kevin Purnelle
    permalink
    0 Comments
  • if(win.children)
        {
            while(win.children.length != 0)
            {
                var len = win.children.length;
                win.remove( win.children[0] );
            }
        }
    
    — answered July 6th 2012 by Ahmed KLABI
    permalink
    0 Comments
  • This seems to work a little better:

    while (thisView.children) {
        try {
            thisView.remove(thisView.children[0]);
            wait(10);
        } catch (e) {
        }
    }
    

    It appears that it takes a finite amount of time to actually remove the child from the view so add a bit of a wait (10 milliseconds worked for me), using a function like this:

    function wait (millis) {
        var date = new Date();
        var curDate = null;
    
        do { 
            curDate = new Date(); 
        } while(curDate-date < millis);
    }
    
    — answered November 30th 2011 by Mark Pemburn
    permalink
    0 Comments
  • what I do in my app, with Alloy, using underscore.js, is this:

    _.each($.myScroll.children, function(view) {
            $.myScroll.remove(view);
     });
    

    Might not be the fastest but easy to understand.

    — answered April 3rd 2013 by Kevin Purnelle
    permalink
    0 Comments
The ownership of individual contributions to this community generated content is retained by the authors of their contributions.
All trademarks remain the property of the respective owner.