API  1.0.0
CPViewController.j
Go to the documentation of this file.
1 /*
2  * CPViewController.j
3  * AppKit
4  *
5  * Created by Nicholas Small and Francisco Tolmasky.
6  * Copyright 2009, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 
26 @global CPApp
27 
28 
30 
63 @implementation CPViewController : CPResponder
64 {
65  CPView _view;
66  BOOL _isLoading;
67  BOOL _isLazy;
68  BOOL _isViewLoaded;
69 
70  id _representedObject;
71  CPString _title;
72 
73  CPString _cibName;
74  CPBundle _cibBundle;
75  CPDictionary _cibExternalNameTable;
76 }
77 
78 + (void)initialize
79 {
80  if (self !== [CPViewController class])
81  return;
82 
84 }
85 
89 - (id)init
90 {
91  return [self initWithCibName:nil bundle:nil];
92 }
93 
94 - (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil
95 {
96  return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:nil];
97 }
98 
99 - (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil owner:(id)anOwner
100 {
101  return [self initWithCibName:aCibNameOrNil bundle:aCibBundleOrNil externalNameTable:@{ CPCibOwner: anOwner }];
102 }
103 
117 - (id)initWithCibName:(CPString)aCibNameOrNil bundle:(CPBundle)aCibBundleOrNil externalNameTable:(CPDictionary)anExternalNameTable
118 {
119  self = [super init];
120 
121  if (self)
122  {
123  // Don't load the cib until someone actually requests the view. The user may just be intending to use setView:.
124  _cibName = aCibNameOrNil;
125  _cibBundle = aCibBundleOrNil || [CPBundle mainBundle];
126  _cibExternalNameTable = anExternalNameTable || @{ CPCibOwner: self };
127 
128  _isLoading = NO;
129  _isLazy = NO;
130  }
131 
132  return self;
133 }
134 
152 - (void)loadView
153 {
154  if (_view)
155  return;
156 
157  if (_cibName)
158  {
159  // check if a cib is already cached for the current _cibName
160  var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
161 
162  if (!cib)
163  {
164  // if the cib isn't cached yet : fetch it and cache it
165  cib = [[CPCib alloc] initWithCibNamed:_cibName bundle:_cibBundle];
166  [CPViewControllerCachedCibs setObject:cib forKey:_cibName];
167  }
168 
169  [cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
170  }
171  else
172  _view = [CPView new];
173 }
174 
184 - (void)loadViewWithCompletionHandler:(Function/*(view, error)*/)aHandler
185 {
186  if (_view)
187  return;
188 
189  if (_cibName)
190  {
191  // check if a cib is already cached for the current _cibName
192  var cib = [CPViewControllerCachedCibs objectForKey:_cibName];
193 
194  if (!cib)
195  {
196  var cibName = _cibName;
197 
198  if (![cibName hasSuffix:@".cib"])
199  cibName = [cibName stringByAppendingString:@".cib"];
200 
201  // If aBundle is nil, use mainBundle, but ONLY for searching for the nib, not for resources later.
202  var bundle = _cibBundle || [CPBundle mainBundle],
203  url = [bundle _cibPathForResource:cibName];
204 
205  // if the cib isn't cached yet : fetch it and cache it
206  [CPURLConnection sendAsynchronousRequest:[CPURLRequest requestWithURL:url] queue:[CPOperationQueue mainQueue] completionHandler:function(aResponse, aData, anError)
207  {
208  if (anError == nil)
209  {
210  var data = [CPData dataWithRawString:aData],
211  aCib = [[CPCib alloc] _initWithData:data bundle:_cibBundle cibName:_cibName];
212 
213  [CPViewControllerCachedCibs setObject:aCib forKey:_cibName];
214  [aCib instantiateCibWithExternalNameTable:_cibExternalNameTable];
215  [self _viewDidLoadWithCompletionHandler:aHandler];
216  }
217  else
218  {
219  aHandler(nil, anError);
220  }
221  }];
222  }
223  else
224  {
225  [cib instantiateCibWithExternalNameTable:_cibExternalNameTable];
226  [self _viewDidLoadWithCompletionHandler:aHandler];
227  }
228  }
229  else
230  {
231  _view = [CPView new];
232  [self _viewDidLoadWithCompletionHandler:aHandler];
233  }
234 }
235 
243 - (CPView)view
244 {
245  if (!_view)
246  {
247  _isLoading = YES;
248 
249  var cibOwner = [_cibExternalNameTable objectForKey:CPCibOwner];
250 
251  if ([cibOwner respondsToSelector:@selector(viewControllerWillLoadCib:)])
252  [cibOwner viewControllerWillLoadCib:self];
253 
254  [self loadView];
255 
256  if (_view === nil && [cibOwner isKindOfClass:[CPDocument class]])
257  [self setView:[cibOwner valueForKey:@"view"]];
258 
259  if (!_view)
260  {
261  var reason = [CPString stringWithFormat:@"View for %@ could not be loaded from Cib or no view specified. Override loadView to load the view manually.", self];
262 
263  [CPException raise:CPInternalInconsistencyException reason:reason];
264  }
265 
266  if ([cibOwner respondsToSelector:@selector(viewControllerDidLoadCib:)])
267  [cibOwner viewControllerDidLoadCib:self];
268 
269  _isLoading = NO;
270  _isLazy = NO;
271  [self _viewDidLoad];
272  }
273  else if (_isLazy)
274  {
275  _isLazy = NO;
276  [self _viewDidLoad];
277  }
278 
279  return _view;
280 }
281 
282 - (void)_viewDidLoad
283 {
284  [self willChangeValueForKey:"isViewLoaded"];
285  [self viewDidLoad];
286  _isViewLoaded = YES;
287  [self didChangeValueForKey:"isViewLoaded"];
288 }
289 
290 - (void)_viewDidLoadWithCompletionHandler:(Function)aHandler
291 {
292  [self _registerOrUnregister:YES notificationsForView:_view];
293 
294  [self willChangeValueForKey:"isViewLoaded"];
295  aHandler(_view, nil);
296  _isViewLoaded = YES;
297  [self didChangeValueForKey:"isViewLoaded"];
298 }
299 
308 - (void)viewDidLoad
309 {
310 
311 }
312 
327 - (void)viewWillAppear
328 {
329 
330 }
331 
346 - (void)viewDidAppear
347 {
348 
349 }
350 
365 - (void)viewWillDisappear
366 {
367 
368 }
369 
382 - (void)viewDidDisappear
383 {
384 
385 }
386 
395 - (void)setView:(CPView)aView
396 {
397  var willChangeIsViewLoaded = (_isViewLoaded == NO && aView != nil) || (_isViewLoaded == YES && aView == nil);
398 
399  [self _registerOrUnregister:NO notificationsForView:_view];
400  [self _registerOrUnregister:YES notificationsForView:aView];
401 
402  if (willChangeIsViewLoaded)
403  [self willChangeValueForKey:"isViewLoaded"];
404 
405  _view = aView;
406  _isViewLoaded = aView !== nil;
407 
408  if (willChangeIsViewLoaded)
409  [self didChangeValueForKey:"isViewLoaded"];
410 }
411 
412 - (BOOL)automaticallyNotifiesObserversOfIsViewLoaded
413 {
414  return NO;
415 }
416 
417 - (void)_registerOrUnregister:(BOOL)shouldRegister notificationsForView:(CPView)aView
418 {
419  if (aView === nil)
420  return;
421 
422  var center = [CPNotificationCenter defaultCenter],
423  notifs_to_sel = @{_CPViewWillAppearNotification : @"viewWillAppear",
424  _CPViewDidAppearNotification : @"viewDidAppear",
425  _CPViewWillDisappearNotification : @"viewWillDisappear",
426  _CPViewDidDisappearNotification : @"viewDidDisappear"};
427 
428  [notifs_to_sel enumerateKeysAndObjectsUsingBlock:function(notif, selString, stop)
429  {
430  var selector = CPSelectorFromString(selString);
431  if ([self implementsSelector:selector])
432  {
433  if (shouldRegister)
434  [center addObserver:self selector:selector name:notif object:aView];
435  else
436  [center removeObserver:self name:notif object:aView];
437  }
438  }];
439 }
440 
441 @end
442 
443 
444 var CPViewControllerViewKey = @"CPViewControllerViewKey",
445  CPViewControllerTitleKey = @"CPViewControllerTitleKey",
446  CPViewControllerCibNameKey = @"CPViewControllerCibNameKey",
447  CPViewControllerBundleKey = @"CPViewControllerBundleKey";
448 
450 
456 - (id)initWithCoder:(CPCoder)aCoder
457 {
458  self = [super initWithCoder:aCoder];
459 
460  if (self)
461  {
462  _view = [aCoder decodeObjectForKey:CPViewControllerViewKey];
463  _title = [aCoder decodeObjectForKey:CPViewControllerTitleKey];
464  _cibName = [aCoder decodeObjectForKey:CPViewControllerCibNameKey];
465 
466  var bundlePath = [aCoder decodeObjectForKey:CPViewControllerBundleKey];
467  _cibBundle = bundlePath ? [CPBundle bundleWithPath:bundlePath] : [CPBundle mainBundle];
468 
469  _cibExternalNameTable = @{ CPCibOwner: self };
470  _isLazy = YES;
471  }
472 
473  return self;
474 }
475 
480 - (void)encodeWithCoder:(CPCoder)aCoder
481 {
482  [super encodeWithCoder:aCoder];
483 
484  [aCoder encodeObject:_view forKey:CPViewControllerViewKey];
485  [aCoder encodeObject:_title forKey:CPViewControllerTitleKey];
486  [aCoder encodeObject:_cibName forKey:CPViewControllerCibNameKey];
487  [aCoder encodeObject:[_cibBundle bundlePath] forKey:CPViewControllerBundleKey];
488 }
489 
490 @end
491 
493 
497 - (CPView)view
498 {
499  return _view;
500 }
501 
505 - (void)setView:(CPView)aValue
506 {
507  _view = aValue;
508 }
509 
513 - (BOOL)isViewLoaded
514 {
515  return _isViewLoaded;
516 }
517 
521 - (id)representedObject
522 {
523  return _representedObject;
524 }
525 
529 - (void)setRepresentedObject:(id)aValue
530 {
531  _representedObject = aValue;
532 }
533 
537 - (CPString)title
538 {
539  return _title;
540 }
541 
545 - (void)setTitle:(CPString)aValue
546 {
547  _title = aValue;
548 }
549 
553 - (CPString)cibName
554 {
555  return _cibName;
556 }
557 
561 - (CPBundle)cibBundle
562 {
563  return _cibBundle;
564 }
565 
569 - (CPDictionary)cibExternalNameTable
570 {
571  return _cibExternalNameTable;
572 }
573 
574 @end
Used to implement exception handling (creating & raising).
Definition: CPException.h:2
id initWithCoder:(CPCoder aCoder)
Definition: CPResponder.j:373
void willChangeValueForKey:(CPString aKey)
id init()
Definition: CALayer.j:126
Definition: CPCib.h:2
A Cappuccino wrapper for any data type.
Definition: CPData.h:2
void raise:reason:(CPString aName, [reason] CPString aReason)
Definition: CPException.j:66
Provides loading of a URL request.
var CPViewControllerViewKey
void encodeWithCoder:(CPCoder aCoder)
Definition: CPResponder.j:390
CPNotificationCenter defaultCenter()
A mutable key-value pair collection.
Definition: CPDictionary.h:2
id requestWithURL:(CPURL aURL)
Definition: CPURLRequest.j:56
var CPViewControllerBundleKey
global CPApp var CPViewControllerCachedCibs
An immutable string (collection of characters).
Definition: CPString.h:2
CPBundle mainBundle()
Definition: CPBundle.j:82
void setView:(CPView aView)
CPData dataWithRawString:(CPString aString)
Definition: CPData.j:45
CPOperationQueue mainQueue()
void didChangeValueForKey:(CPString aKey)
CPURLConnection sendAsynchronousRequest:queue:completionHandler:(CPURLRequest aRequest, [queue] CPOperationQueue aQueue, [completionHandler] Function aHandler)
CPCibOwner
Definition: CPCib.j:25
Defines methods for use when archiving & restoring (enc/decoding).
Definition: CPCoder.h:2
id init()
Definition: CPObject.j:145
Sends messages (CPNotification) between objects.
CPBundle bundleWithPath:(CPString aPath)
Definition: CPBundle.j:54
Contains data obtained during a request made with CPURLConnection.
Definition: CPURLRequest.h:2
id initWithCibName:bundle:externalNameTable:(CPString aCibNameOrNil, [bundle] CPBundle aCibBundleOrNil, [externalNameTable] CPDictionary anExternalNameTable)
id initWithCibName:bundle:(CPString aCibNameOrNil, [bundle] CPBundle aCibBundleOrNil)
var CPViewControllerCibNameKey
var CPViewControllerTitleKey
id initWithCibNamed:bundle:(CPString aName, [bundle] CPBundle aBundle)
Definition: CPCib.j:95
id alloc()
Definition: CPObject.j:130
Represents an operation queue that can run CPOperations.
Definition: CPView.j:137
id stringWithFormat:(CPString format, [,] ...)
Definition: CPString.j:166