Creating subviews programmatically ?

  • As an Obj C Newbie,  I'm having trouble understanding subviews. In a
    former life I wrote (in C) a Scheduling program that displayed a
    calendar of one of many possible work schedules for a month in a
    window. It drew each schedule over a calendar grid straight into a
    single view.

    Being new to Obj C, I was thinking of re-writing this and have each
    day in the calendar window "know" how to draw itself, the date, and
    any schedule info for that day.  I want to create subviews within the
    main view for each day in the calendar, and have them remain in a grid
    and redraw text and graphical info, resizing if the window is re-sized.

    Is this possible or would the overhead and drawing time be
    prohibited?  Would I start by adding subviews to the main view ,
    giving each it's own bounds rect, then resize the individual subviews
    as the main view was re-sized?  Is auto sizing taken care of by the
    main view?

    I don't know where to start with this... can anyone give me a few
    hints on a practical approach?

    Thanks in advance

    Vince
  • On Nov 26, 2007, at 8:18 AM, Vince Ackerman wrote:

    > Is this possible or would the overhead and drawing time be prohibited?

    In all likelihood quite possible.

    > Would I start by adding subviews to the main view , giving each it's
    > own bounds rect, then resize the individual subviews as the main
    > view was re-sized?

    If you replace "bounds rect" with "frame rect", the above statement
    seems about right.

    > Is auto sizing taken care of by the main view?

    In this case I think that you would have to take care of sizing and
    positioning of subviews manually as the size of the main view changes.

    j o a r
  • > Is this possible or would the overhead and drawing time be
    > prohibited?  Would I start by adding subviews to the main view ,
    > giving each it's own bounds rect, then resize the individual subviews
    > as the main view was re-sized?  Is auto sizing taken care of by the
    > main view?

      Of course it's possible to do this but why? The overhead would be
    greater for 30-some individual "day" views.

      If you have a "Calendar View" that knows its own grid, why not have
    routines something like this:

    - (void)drawRect:(NSRect)rect  // draws portion of the overall view
    - (void)drawDay:(id)someDayObject inRect:(NSRect)rect // draws a day
    object (that knows how to draw its own subparts) in a given rect

      The -drawRect: method would determine which day rects need redrawn
    (based on the rect Cocoa asked it to draw) and draw each "day" object
    into its designated rect. Your "day" object (or the calendar view) can
    then break the task down further:

    // Assume this method belongs to your "Day" class
    - (void)drawView:(id)someView inRect:(NSRect)rect
    selected:(BOOL)selected ... other options...
    {
      // Lock focus on "someView"
      // If I'm selected, draw a "selected" color background ...
      // Draw my date number ...
      // For every event, draw it in its own color ...
      // Unlock focus from "someView"
      // ...
    }

      Of course the proper way to do this is to use NSControl and NSCell
    subclasses (ie a "Calendar" control subclass and a "Day" cell
    subclass). This will make it easier to handle selection state,
    mouse/key events, etc.

      In its simplest sense, however, look at the Sketch example in your
    /Developer/Examples folder. This does the same thing with a "Canvas"
    and "Shape" objects, where a class of "Shape" knows how to draw itself
    in a rect of a view with certain options when asked.

    --
    I.S.
  • Okay, thanks. I was hoping to avoid going down a particular road and
    then end up back-tracking and re-doing a lot of work later.

    I do want to be able to intercept mouse over events and have pop up
    (tool tips?) box with a details about the schedule for that day.
    That's why I thought of subviews...

    I'm still trying to get my (old) brain around Obj C and Cocoa.

    I'll check out the example you referenced.

    Vince

    On Nov 26, 2007, at 08:31, I. Savant wrote:

    >> Is this possible or would the overhead and drawing time be
    >> prohibited?  Would I start by adding subviews to the main view ,
    >> giving each it's own bounds rect, then resize the individual subviews
    >> as the main view was re-sized?  Is auto sizing taken care of by the
    >> main view?
    >
    > Of course it's possible to do this but why? The overhead would be
    > greater for 30-some individual "day" views.
    >
    > If you have a "Calendar View" that knows its own grid, why not have
    > routines something like this:
    >
    > - (void)drawRect:(NSRect)rect  // draws portion of the overall view
    > - (void)drawDay:(id)someDayObject inRect:(NSRect)rect // draws a day
    > object (that knows how to draw its own subparts) in a given rect
    >
    > The -drawRect: method would determine which day rects need redrawn
    > (based on the rect Cocoa asked it to draw) and draw each "day" object
    > into its designated rect. Your "day" object (or the calendar view) can
    > then break the task down further:
    >
    > // Assume this method belongs to your "Day" class
    > - (void)drawView:(id)someView inRect:(NSRect)rect
    > selected:(BOOL)selected ... other options...
    > {
    > // Lock focus on "someView"
    > // If I'm selected, draw a "selected" color background ...
    > // Draw my date number ...
    > // For every event, draw it in its own color ...
    > // Unlock focus from "someView"
    > // ...
    > }
    >
    > Of course the proper way to do this is to use NSControl and NSCell
    > subclasses (ie a "Calendar" control subclass and a "Day" cell
    > subclass). This will make it easier to handle selection state,
    > mouse/key events, etc.
    >
    > In its simplest sense, however, look at the Sketch example in your
    > /Developer/Examples folder. This does the same thing with a "Canvas"
    > and "Shape" objects, where a class of "Shape" knows how to draw itself
    > in a rect of a view with certain options when asked.
    >
    > --
    > I.S.
  • I disagree with the previous answers.  I see no problem with 31 subviews from a performance perspective.  Things like cursor rects and event handling will be nicer and simpler if each "day" is its own view.

      I would create a MYCalendarView class that uses a template MYDayControl instance.  The MYCalendarView copies the template MYDayControl instance as many times as needed to populate the calendar.  MYCalendarView should have a -tile method that repositions and re-sizes the MYDayControl instances as needed.  Create the template MYDayControl instance in Interface Builder so that days can bee structured however you want...the MYCalendarView class is reusable with multiple different kinds of MYDayControls.  MYCalendarView could show a work-week or a whole month or a single day and use different specialized MYDayControl instance for each style...

      This opens lots of cool possibilities such as CoreAnimation effects to indicate selection.  The user double clicks a day and it zooms to fill the whole MYCalendarView with a nice animation... moving tasks or events within a day or from day to day could use standard NSView support for drag and drop.  Each MYCalendarView instance could preserve a cached snapshot of itself so that if multiple months are shown at the same time, each moth would look like a miniature preview of itself the same way document icons in the dock show a preview...

      You might even be able to make this work using the now standard NSCollectionView.
      http://developer.apple.com/documentation/Cocoa/Reference/NSCollectionView_C
    lass/Introduction/Introduction.html
  • > I disagree with the previous answers.  I see no problem with 31 subviews from a performance perspective.  Things like cursor rects and event handling will be nicer and simpler if each "day" is its own view.

      I disagree with your disagreement and raise you a pedantic correction. ;-)

      First, the pedantic correction. Scott Anguish enjoyed correcting me
    by saying that's not really Core Animation but layer backed views. I
    countered with: "... which in turn uses Core Animation, hence
    'CABasicAnimation' ..." but he has yet to recant.

      Your move, Anguish.

      < To all but Scott who knows better, that is, of course, a joke. ;-)  >

      I still tend to think of the calendar itself as a control and the
    days as cells. There's nothing stopping you from using
    "core-layer-animated-backed-views" ( :-P ) in the scenarios you've
    suggested, but I can't disagree that it'd be easier to use a prototype
    view and perhaps an NSCollectionView.

      The drag and drop scenario, however, really wouldn't be any 'easier'
    just because they're separate views. It's "dragged from another view
    into myself and this is the day I represent" versus "dragged from
    another day's rect into this day's rect amongst the days I represent".
    Either way works just fine.

    --
    I.S.

    ... who - in all seriousness - bows to the far-superior skills of
    those he's currently harassing ...
  • On Nov 26, 2007, at 3:35 PM, I. Savant wrote:

    >> I disagree with the previous answers.  I see no problem with 31
    >> subviews from a performance perspective.  Things like cursor rects
    >> and event handling will be nicer and simpler if each "day" is its
    >> own view.
    >
    > I disagree with your disagreement and raise you a pedantic
    > correction. ;-)

    <shakes his head and backs slowly away from the crazy man>

    > I still tend to think of the calendar itself as a control and the
    > days as cells. There's nothing stopping you from using
    > "core-layer-animated-backed-views" ( :-P ) in the scenarios you've
    > suggested, but I can't disagree that it'd be easier to use a prototype
    > view and perhaps an NSCollectionView.

    I don't think NSCollectionView is the way to go here.  You'd need to
    pad it with some empty views for the leading and trailing 'other
    month' days.

    Erik is right that views for each day would be the simplest way to
    implement it.

    You may think of it as a single control with cells, but you really
    don't get any advantage from abstracting the days out as cells. You'd
    not re-use a day cell on its own, for example in a table or matrix. So
    you're adding complexity. As a result, I don't know that it'd be worth
    the extra effort.

    You could distribute IdiotCalendarView* that still uses
    IdiotCalendarDayView for each of the underlying days but provides an
    API that makes it unnecessary to be aware of, or deal with, the
    underlying subviews

    *I'm assuming you prefix all your classes with the unique "Idiot"
    prefix..  [now who's joking? :-)]
  • On Nov 26, 2007, at 7:16 PM, Scott Anguish wrote:

    > You may think of it as a single control with cells, but you really
    > don't get any advantage from abstracting the days out as cells.
    > You'd not re-use a day cell on its own, for example in a table or
    > matrix. So you're adding complexity. As a result, I don't know that
    > it'd be worth the extra effort.

    Having separate views will make it tedious to adopt accessibility.
    Especially if individual days can be interacted with.  To the
    accessibility framework, users will end up with 28 to 31 view
    objects.  Tabbing amongst them would be very tedious.  If they support
    full keyboard access, Tab (and Shift-Tab) would be required to
    navigate amongst them.  But tabbing 30 times over to go from the 1st
    to the 31st would be a mess.

    Compare this to a single control with multiple cells.  The concept of
    focus would be applied to a single cell.  You can then tap into
    move[Up|Down|Left|Right]: to move the selection more naturally and get
    to an appropriate destination quicker.

    You also gain a huge benefit in that multiple cells could be selected
    at once (e.g. maybe you'd want to select an entire week, or all
    Wednesdays).

    ___________________________________________________________
    Ricky A. Sharp        mailto:<rsharp...>
    Instant Interactive(tm)  http://www.instantinteractive.com
  • On Nov 26, 2007, at 8:16 PM, Scott Anguish wrote:

    > *I'm assuming you prefix all your classes with the unique "Idiot"
    > prefix..  [now who's joking? :-)]

      Actually, that's "IdioticCalendarView", thanks. ;-)

    --
    I.S.
  • After reading all of your interesting ideas I was looking at the
    developer sample code for Animate Views and think that the simple grid
    layout does in some ways what I need. I don't really care about
    animating the individual days as I build the calendar initially, but
    allowing the user to click on a schedule that covers 2 or 3 days and
    have a larger view zoom open showing the detail for this "job" in a
    new window or expand the same window would be great. Don't know if
    NSCollectionView would allow that?

    I can't find any examples of NSCollectionView doing anything like
    that.  Is there any other documentation other than the one page
    referenced earlier?

    Thanks for all the great ideas. You guys are very helpful.

    Vince

    On Nov 26, 2007, at 17:57, I. Savant wrote:

    > On Nov 26, 2007, at 8:16 PM, Scott Anguish wrote:
    >
    >> *I'm assuming you prefix all your classes with the unique "Idiot"
    >> prefix..  [now who's joking? :-)]
    >
    > Actually, that's "IdioticCalendarView", thanks. ;-)
    >
    > --
    > I.S.
  • Greetings,

    Can NSView re-order it's array of subviews without notice?

    I'll try to explain my problem with out sounding like the total newbie
    that I am...

    I've programmatically  created a (calendar type) grid of sub-classed
    subviews in my main view, and assigned each one a number (to keep
    track of them). There are 42 subviews, numbered 0 - 41.

    They all display and draw correctly and in order in the main view.
    However, when I resize the window, I've found an odd problem.

    When the main view gets a "setFrameSize" call, I override it and call
    a method to re-size the subview frames. This method uses NSEnumerator
    to step through the NSArray returned by "subViews" and recalculates
    and calls setFrame on each subview.

    For some reason each time the resizeSubview method is called,
    NSEnumerator alternately returns with either the first subview in the
    array, or the last. The first time the method is called, the first
    subview is number 0, continuing through 41. The next time it goes
    through the array, the enumerator starts with subview 41, skips
    subview 0, and iterates to subview 1, then 2, 3 etc.

    I'm doing nothing more than changing the frameRect of each subview, so
    I can't understand why the array is changing order.

    Could someone clue me in?

    Thanks,

    Vince
  • On Nov 30, 2007, at 09:49, Vince Ackerman wrote:
    >
    > For some reason each time the resizeSubview method is called,
    > NSEnumerator alternately returns with either the first subview in
    > the array, or the last. The first time the method is called, the
    > first subview is number 0, continuing through 41. The next time it
    > goes through the array, the enumerator starts with subview 41, skips
    > subview 0, and iterates to subview 1, then 2, 3 etc.
    >

    I should have added that when it starts with subview 41, then iterates
    to subview 1, 2, 3 etc., it ends with subview 0.

    Vince
  • Excuse my sloppy code... but this was my kludge to test if I could
    resize the grid of subviews.

    -(void)resizeDayViews: (NSSize) size  //  Adjust all subViews for size
    of Main view
    {
    NSArray *subDayViews = [self subviews];  // get array of all subviews
    NSEnumerator *enumerator = [subDayViews objectEnumerator]; // get
    enumerator to step through the subview array

    NSRect newBounds = [self bounds];  // get current bounds rect
    NSRect newRect;

    NSLog( @"Start of Resize Method------------------------------");
    float dayWidth = ( (newBounds.size.width - 9) / 7);
    float weekHeight = ( (newBounds.size.height - 9) / 6) ;
    int x,y,rowCnt, columnCnt;
    // for each row of days
    for (y = 5, rowCnt = 0;  rowCnt < 6;    y = y + weekHeight, rowCnt++)
    {
      for(x = 5, columnCnt = 0; columnCnt < 7; x = x + dayWidth, columnCnt+
    +)
      {
      newRect = NSMakeRect(x, y, dayWidth, weekHeight );
      DayView *tempView = [enumerator nextObject];
      [ tempView setFrame: newRect ];
      NSLog( @"frame set");
      NSLog( @"%@", [NSString stringWithFormat:@"%i", [tempView
    gridNum] ]);
      NSLog(@"%@", NSStringFromRect(newRect) );
      NSLog(@"%@", NSStringFromRect([ tempView frame]) );
      }

    }
    }

    On Nov 30, 2007, at 09:55, Joshua Emmons wrote:

    >> For some reason each time the resizeSubview method is called,
    >> NSEnumerator alternately returns with either the first subview in
    >> the array, or the last. The first time the method is called, the
    >> first subview is number 0, continuing through 41. The next time it
    >> goes through the array, the enumerator starts with subview 41,
    >> skips subview 0, and iterates to subview 1, then 2, 3 etc.
    >
    > I haven't come across this behavior before, and a quick sample app
    > doesn't exhibit it. Maybe give some more details about your
    > implementation? Maybe it's a bug with how your calling NSEnumerator?
    >
    > Cheers,
    > -Joshua Emmons
  • Sorry, I was using Bounds instead of Frame...  but it still doesn't
    change the behavior with the array. Here are the two methods I'm using:

    /* Changing frame (which is what happens when the window is resized)
    should cause relayout.
      */
    - (void)setFrameSize:(NSSize)size {
        [super setFrameSize:size];
        [self resizeDayViews: size];
    }

    -(void)resizeDayViews: (NSSize) size  //  Adjust all subViews for size
    of Main view
    {
    NSArray *subDayViews = [self subviews];  // get array of all subviews
    NSEnumerator *enumerator = [subDayViews objectEnumerator]; // get
    enumerator to step through the subview array

    NSRect newFrame = [self frame];  // get current bounds rect
    NSRect newRect;


    float dayWidth = ( (newFrame.size.width - 9) / 7);
    float weekHeight = ( (newFrame.size.height - 9) / 6) ;
    int x,y,rowCnt, columnCnt;

    NSLog( @"Start of Resize Method------------------------------");

    // for each row of days
    for (y = 5, rowCnt = 0;  rowCnt < 6;    y = y + weekHeight, rowCnt++)
    {
      for(x = 5, columnCnt = 0; columnCnt < 7; x = x + dayWidth, columnCnt+
    +)
      {
      newRect = NSMakeRect(x, y, dayWidth, weekHeight );
      DayView *tempView = [enumerator nextObject];
      [ tempView setFrame: newRect ];
      NSLog( @"frame set");
      NSLog( @"%@", [NSString stringWithFormat:@"%i", [tempView
    gridNum] ]);
      NSLog(@"%@", NSStringFromRect(newRect) );

      NSLog(@"%@", NSStringFromRect([ tempView frame]) );
      }

    }
    }

    On Nov 30, 2007, at 09:55, Joshua Emmons wrote:

    >> For some reason each time the resizeSubview method is called,
    >> NSEnumerator alternately returns with either the first subview in
    >> the array, or the last. The first time the method is called, the
    >> first subview is number 0, continuing through 41. The next time it
    >> goes through the array, the enumerator starts with subview 41,
    >> skips subview 0, and iterates to subview 1, then 2, 3 etc.
    >
    > I haven't come across this behavior before, and a quick sample app
    > doesn't exhibit it. Maybe give some more details about your
    > implementation? Maybe it's a bug with how your calling NSEnumerator?
    >
    > Cheers,
    > -Joshua Emmons
  • Sorry to bump this, but I'm totally out of ideas on what I'm doing
    wrong. Can anyone see anything incorrect about my use of NSEnumerator ??

    Vince

    On Nov 30, 2007, at 10:18, Vince Ackerman wrote:

    > Sorry, I was using Bounds instead of Frame...  but it still doesn't
    > change the behavior with the array. Here are the two methods I'm
    > using:
    >
    > /* Changing frame (which is what happens when the window is resized)
    > should cause relayout.
    > */
    > - (void)setFrameSize:(NSSize)size {
    > [super setFrameSize:size];
    > [self resizeDayViews: size];
    > }
    >
    >
    > -(void)resizeDayViews: (NSSize) size  //  Adjust all subViews for
    > size of Main view
    > {
    > NSArray *subDayViews = [self subviews];  // get array of all subviews
    > NSEnumerator *enumerator = [subDayViews objectEnumerator]; // get
    > enumerator to step through the subview array
    >
    > NSRect newFrame = [self frame];            // get current bounds rect
    > NSRect newRect;
    >
    >
    > float dayWidth = ( (newFrame.size.width - 9) / 7);
    > float weekHeight = ( (newFrame.size.height - 9) / 6) ;
    > int x,y,rowCnt, columnCnt;
    >
    > NSLog( @"Start of Resize Method------------------------------");
    >
    > // for each row of days
    > for (y = 5, rowCnt = 0;  rowCnt < 6;    y = y + weekHeight, rowCnt+
    > +)
    > {
    > for(x = 5, columnCnt = 0; columnCnt < 7; x = x + dayWidth,
    > columnCnt++)
    > {
    > newRect = NSMakeRect(x, y, dayWidth, weekHeight );
    > DayView *tempView = [enumerator nextObject];
    > [ tempView setFrame: newRect ];
    > NSLog( @"frame set");
    > NSLog( @"%@", [NSString stringWithFormat:@"%i", [tempView
    > gridNum] ]);
    > NSLog(@"%@", NSStringFromRect(newRect) );
    >
    > NSLog(@"%@", NSStringFromRect([ tempView frame]) );
    > }
    >
    > }
    > }
    >
    >
    > On Nov 30, 2007, at 09:55, Joshua Emmons wrote:
    >
    >>> For some reason each time the resizeSubview method is called,
    >>> NSEnumerator alternately returns with either the first subview in
    >>> the array, or the last. The first time the method is called, the
    >>> first subview is number 0, continuing through 41. The next time it
    >>> goes through the array, the enumerator starts with subview 41,
    >>> skips subview 0, and iterates to subview 1, then 2, 3 etc.
    >>
    >> I haven't come across this behavior before, and a quick sample app
    >> doesn't exhibit it. Maybe give some more details about your
    >> implementation? Maybe it's a bug with how your calling NSEnumerator?
    >>
    >> Cheers,
    >> -Joshua Emmons

  • > On Nov 30, 2007, at 10:18, Vince Ackerman wrote:
    >
    >> Sorry, I was using Bounds instead of Frame...  but it still
    >> doesn't change the behavior with the array. Here are the two
    >> methods I'm using:
    >>
    >> /* Changing frame (which is what happens when the window is
    >> resized) should cause relayout.
    >> */
    >> - (void)setFrameSize:(NSSize)size {
    >> [super setFrameSize:size];
    >> [self resizeDayViews: size];
    >> }
    >>
    >>
    >> -(void)resizeDayViews: (NSSize) size  //  Adjust all subViews for
    >> size of Main view
    >> {
    >> NSArray *subDayViews = [self subviews];  // get array of all
    >> subviews
    >> NSEnumerator *enumerator = [subDayViews objectEnumerator]; // get
    >> enumerator to step through the subview array
    >>
    >> NSRect newFrame = [self frame];            // get current bounds rect
    >> NSRect newRect;
    >>
    >>
    >> float dayWidth = ( (newFrame.size.width - 9) / 7);
    >> float weekHeight = ( (newFrame.size.height - 9) / 6) ;
    >> int x,y,rowCnt, columnCnt;
    >>
    >> NSLog( @"Start of Resize Method------------------------------");
    >>
    >> // for each row of days
    >> for (y = 5, rowCnt = 0;  rowCnt < 6;    y = y + weekHeight,
    >> rowCnt++)
    >> {
    >> for(x = 5, columnCnt = 0; columnCnt < 7; x = x + dayWidth,
    >> columnCnt++)
    >> {
    >> newRect = NSMakeRect(x, y, dayWidth, weekHeight );
    >> DayView *tempView = [enumerator nextObject];
    >> [ tempView setFrame: newRect ];
    >> NSLog( @"frame set");
    >> NSLog( @"%@", [NSString stringWithFormat:@"%i", [tempView
    >> gridNum] ]);
    >> NSLog(@"%@", NSStringFromRect(newRect) );
    >>
    >> NSLog(@"%@", NSStringFromRect([ tempView frame]) );
    >> }
    >>
    >> }
    >> }
    >>
    >>
    >> On Nov 30, 2007, at 09:55, Joshua Emmons wrote:
    >>
    >>>> For some reason each time the resizeSubview method is called,
    >>>> NSEnumerator alternately returns with either the first subview
    >>>> in the array, or the last. The first time the method is called,
    >>>> the first subview is number 0, continuing through 41. The next
    >>>> time it goes through the array, the enumerator starts with
    >>>> subview 41, skips subview 0, and iterates to subview 1, then 2,
    >>>> 3 etc.
    >>>
    >>> I haven't come across this behavior before, and a quick sample
    >>> app doesn't exhibit it. Maybe give some more details about your
    >>> implementation? Maybe it's a bug with how your calling NSEnumerator?
    >>>
    >>> Cheers,
    >>> -Joshua Emmons
    >>

    Have you tried avoiding the enumerator entirely and just using
    another index var and objectAtIndex: to read the subDayViews array?
    I'm having trouble believing that this is a problem with
    NSEnumerator... I have a feeling your array is not what you expect.
    In fact, if you haven't already, I'd try dumping the array's
    description to the log.

    The only other thing I can think of is that maybe you've got this
    code in the class for the views that you're working on, and your call to

    [ tempView setFrame: newRect ]

    is causing your setFrameSize override to be called, which is calling
    resizeDayViews again, giving you misleading log statements and
    general chaos. In other words, are you sure you're not reentering the
    method because of that setFrame call in the loop (or something else)?
    I have no idea if setFrame causes the setFrameSize override to be
    called, but it wouldn't surprise me. You may want to log entry and
    exit for the method, or step through the loop in the debugger.

    Anyway, just some late night thoughts that may or may not help.

    - d
  • >
    > Have you tried avoiding the enumerator entirely and just using
    > another index var and objectAtIndex: to read the subDayViews array?
    > I'm having trouble believing that this is a problem with
    > NSEnumerator... I have a feeling your array is not what you expect.
    > In fact, if you haven't already, I'd try dumping the array's
    > description to the log.
    >
    > The only other thing I can think of is that maybe you've got this
    > code in the class for the views that you're working on, and your
    > call to
    >
    > [ tempView setFrame: newRect ]
    >
    > is causing your setFrameSize override to be called, which is calling
    > resizeDayViews again, giving you misleading log statements and
    > general chaos. In other words, are you sure you're not reentering
    > the method because of that setFrame call in the loop (or something
    > else)? I have no idea if setFrame causes the setFrameSize override
    > to be called, but it wouldn't surprise me. You may want to log entry
    > and exit for the method, or step through the loop in the debugger.
    >
    > Anyway, just some late night thoughts that may or may not help.
    >

    I put a NSLog () in the setFrameSize: override and it's only being
    called once, so that wouldn't explain it.  The description dump of the
    array each time through the resizeDayView method shows that it is in
    fact switching the first and last item in the array:

      <DayView: 0x13ba80>,    <----------------  this is the original
    item zero
        <DayView: 0x13dab0>,
        <DayView: 0x13dce0>,
      ........  37 more subView items ...
        <DayView: 0x147650>,
        <DayView: 0x147a60>  <----------------  this is the original
    last item 41

    As the window continues its resize, the description dump shows it
    alternates to this:

        <DayView: 0x147a60>,  <----------------  this is the original
    item 41
        <DayView: 0x13dab0>,
        <DayView: 0x13dce0>,
    ........  37 more subView items ...
        <DayView: 0x147650>,
        <DayView: 0x13ba80>  <----------------  this is the original item
    zero, now at the end of the array

    This happens every other time through the resizeDayView method.
    Strange huh?

    Next, I'll try getting rid of NSEnumerator as you suggest.  For the
    life of me I can't see why the array is being switched.

    Thanks

    Vince
  • On Dec 1, 2007, at 11:51 AM, Vince Ackerman wrote:
    > I put a NSLog () in the setFrameSize: override and it's only being
    > called once, so that wouldn't explain it.  The description dump of
    > the array each time through the resizeDayView method shows that it
    > is in fact switching the first and last item in the array:
    >
    > <DayView: 0x13ba80>,    <----------------  this is the original
    > item zero
    > <DayView: 0x13dab0>,
    > <DayView: 0x13dce0>,
    > ........  37 more subView items ...
    > <DayView: 0x147650>,
    > <DayView: 0x147a60>  <----------------  this is the original
    > last item 41
    >
    >
    > As the window continues its resize, the description dump shows it
    > alternates to this:
    >
    >
    > <DayView: 0x147a60>,  <----------------  this is the original
    > item 41
    > <DayView: 0x13dab0>,
    > <DayView: 0x13dce0>,
    > ........  37 more subView items ...
    > <DayView: 0x147650>,
    > <DayView: 0x13ba80>  <----------------  this is the original
    > item zero, now at the end of the array
    >
    >
    > This happens every other time through the resizeDayView method.
    > Strange huh?
    >
    > Next, I'll try getting rid of NSEnumerator as you suggest.  For the
    > life of me I can't see why the array is being switched.

    Well, by the descriptions you're showing, it looks like NSEnumerator
    is working correctly and you'll see the same thing from
    objectAtIndex. It must be something about how the subviews are dealt
    with by the system during resizing. There's no guarantee about the
    order in which subviews are drawn, and there may be no guarantee
    about the order they're kept in either. You're probably better off
    just creating an array of these view objects in the order you want at
    awakeFromNib or the like, and using that during the resize. That way
    you don't have to depend on the system maintaining your ordering.

    - d
  • As per your suggestion I've Eliminated the NSEnumerator, and using
    NSArray objectAtIndex: index  shows the same behavior. Aside from
    sending an "addSubview" to the view in my awakeFromNib, and building
    the original grid of subviews, I'm not messing with the subviews
    anywhere else but the resize method.

    The first and last subview in the array are switched each time
    through.  This are my two methods:

    - (void)setFrameSize:(NSSize)size {
    NSLog(@"Override setFrameSize called");
        [super setFrameSize:size];
        [self resizeDayViews: size];
    }

    -(void)resizeDayViews: (NSSize) size  //  Adjust all subViews for size
    of Main view
    {
    NSArray *subDayViews = [self subviews];  // get array of all subviews
    NSRect newFrame = [self frame];  // get current frame rect
    NSRect newRect;
    float dayWidth = ( (newFrame.size.width - 9) / 7);
    float weekHeight = ( (newFrame.size.height - 9) / 6) ;
    int x,y,rowCnt, columnCnt, index;

    NSLog( @"Start of Resize Method------------------------------");
    NSLog( [subDayViews description]);
    // for each row of days
    for (index = 0, y = 5, rowCnt = 0;  rowCnt < 6;    y = y +
    weekHeight, rowCnt++)
    {
      for(x = 5, columnCnt = 0; columnCnt < 7; x = x + dayWidth, columnCnt+
    +)
      {
      newRect = NSMakeRect(x, y, dayWidth, weekHeight );
      DayView *tempView = [subDayViews objectAtIndex: index];
      [ tempView setFrame: newRect ];
      NSLog( @"frame set");
      NSLog( @"%@", [NSString stringWithFormat:@"%i", [tempView
    gridNum] ]);
      NSLog(@"%@", NSStringFromRect(newRect) );
      index++;
      }

    }
    }

    On Nov 30, 2007, at 22:21, Dave Hersey wrote:
    >
    >
    > Have you tried avoiding the enumerator entirely and just using
    > another index var and objectAtIndex: to read the subDayViews array?
    > I'm having trouble believing that this is a problem with
    > NSEnumerator... I have a feeling your array is not what you expect.
    > In fact, if you haven't already, I'd try dumping the array's
    > description to the log.
    >
    > The only other thing I can think of is that maybe you've got this
    > code in the class for the views that you're working on, and your
    > call to
    >
    > [ tempView setFrame: newRect ]
    >
    > is causing your setFrameSize override to be called, which is calling
    > resizeDayViews again, giving you misleading log statements and
    > general chaos. In other words, are you sure you're not reentering
    > the method because of that setFrame call in the loop (or something
    > else)? I have no idea if setFrame causes the setFrameSize override
    > to be called, but it wouldn't surprise me. You may want to log entry
    > and exit for the method, or step through the loop in the debugger.
    >
    > Anyway, just some late night thoughts that may or may not help.
    >
    > - d
  • On Dec 1, 2007, at 09:22, Dave Hersey wrote:
    >
    >
    > Well, by the descriptions you're showing, it looks like NSEnumerator
    > is working correctly and you'll see the same thing from
    > objectAtIndex. It must be something about how the subviews are dealt
    > with by the system during resizing. There's no guarantee about the
    > order in which subviews are drawn, and there may be no guarantee
    > about the order they're kept in either. You're probably better off
    > just creating an array of these view objects in the order you want
    > at awakeFromNib or the like, and using that during the resize. That
    > way you don't have to depend on the system maintaining your ordering.
    >
    > - d
    >
    > _______________________________________________

    Yeah, it looks like NSView is re-ordering the array even though Apples
    Docs suggest they are in a fixed order and you not modify the subview
    array. I guess I could tell the array to sort by the gridNum variable
    I've assigned when each was initialized, then setFrame?

    Here's the docs on the subviews message:

    subviews
    Return the receiver’s immediate subviews.

    - (NSArray *)subviews

    Discussion
    The order of the subviews may be considered as being back-to-front,
    but this does not imply invalidation and drawing behavior. The order
    is based on the order of the receiver's subviews as specified in the
    nib file from which they were unarchived or the programmatic interface
    for modifying the receiver's subview list. This ordering is also the
    reverse of the order in which hit-testing is done.
  • NSView was screwing up the order of it's array of subviews.

    If anyone else runs into this problem my solution that finally worked
    was to sort the subview array held by my NSView before calling
    setFrame. Apples docs do talk about sorting the subview array so I
    guess it's okay to do this, so I used:

    [self sortSubviewsUsingFunction:intSort context: NULL];

    and a function to compare an index I assigned each subview when init'd.

    NSInteger intSort(id view1, id view2, void *context)  // compare
    function to order subview array by gridnum
    {
        int v1 = [view1 gridNum];
        int v2 = [view2 gridNum];
        if (v1 < v2)
            return NSOrderedAscending;
        else if (v1 > v2)
            return NSOrderedDescending;
        else
            return NSOrderedSame;
    }

    Fixed my problem though there is probably a performance hit having to
    do this for a large number of arrays.... I don't know.

    Thanks for the help

    Vince

    On Dec 1, 2007, at 09:22, Dave Hersey wrote:

    >
    > Well, by the descriptions you're showing, it looks like NSEnumerator
    > is working correctly and you'll see the same thing from
    > objectAtIndex. It must be something about how the subviews are dealt
    > with by the system during resizing. There's no guarantee about the
    > order in which subviews are drawn, and there may be no guarantee
    > about the order they're kept in either. You're probably better off
    > just creating an array of these view objects in the order you want
    > at awakeFromNib or the like, and using that during the resize. That
    > way you don't have to depend on the system maintaining your ordering.
    >
    > - d
  • Instead of sorting the array, just take them in the order they're provided,
    and use your gridNum member to determine the frame for each. Something like,
    for each subview, regardless of whether you use objectAtIndex or an
    enumerator:

        xpos = (subvw->gridnum % 7) * dayWidth;
        ypos = (subvw->gridnum / 6) * weekHeight;

    --
    Scott Ribe
    <scott_ribe...>
    http://www.killerbytes.com/
    (303) 722-0567 voice
previous month november 2007 next month
MTWTFSS
      1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30    
Go to today