|
1 // |
|
2 // MGSplitViewController.m |
|
3 // MGSplitView |
|
4 // |
|
5 // Created by Matt Gemmell on 26/07/2010. |
|
6 // Copyright 2010 Instinctive Code. |
|
7 // |
|
8 |
|
9 #import "MGSplitViewController.h" |
|
10 #import "MGSplitDividerView.h" |
|
11 #import "MGSplitCornersView.h" |
|
12 |
|
13 #define MG_DEFAULT_SPLIT_POSITION 320.0 // default width of master view in UISplitViewController. |
|
14 #define MG_DEFAULT_SPLIT_WIDTH 1.0 // default width of split-gutter in UISplitViewController. |
|
15 #define MG_DEFAULT_CORNER_RADIUS 5.0 // default corner-radius of overlapping split-inner corners on the master and detail views. |
|
16 #define MG_DEFAULT_CORNER_COLOR [UIColor blackColor] // default color of intruding inner corners (and divider background). |
|
17 |
|
18 #define MG_PANESPLITTER_CORNER_RADIUS 0.0 // corner-radius of split-inner corners for MGSplitViewDividerStylePaneSplitter style. |
|
19 #define MG_PANESPLITTER_SPLIT_WIDTH 25.0 // width of split-gutter for MGSplitViewDividerStylePaneSplitter style. |
|
20 |
|
21 #define MG_MIN_VIEW_WIDTH 200.0 // minimum width a view is allowed to become as a result of changing the splitPosition. |
|
22 |
|
23 #define MG_ANIMATION_CHANGE_SPLIT_ORIENTATION @"ChangeSplitOrientation" // Animation ID for internal use. |
|
24 #define MG_ANIMATION_CHANGE_SUBVIEWS_ORDER @"ChangeSubviewsOrder" // Animation ID for internal use. |
|
25 |
|
26 |
|
27 @interface MGSplitViewController (MGPrivateMethods) |
|
28 |
|
29 - (void)setup; |
|
30 - (CGSize)splitViewSizeForOrientation:(UIInterfaceOrientation)theOrientation; |
|
31 - (void)layoutSubviews; |
|
32 - (void)layoutSubviewsWithAnimation:(BOOL)animate; |
|
33 - (void)layoutSubviewsForInterfaceOrientation:(UIInterfaceOrientation)theOrientation withAnimation:(BOOL)animate; |
|
34 - (BOOL)shouldShowMasterForInterfaceOrientation:(UIInterfaceOrientation)theOrientation; |
|
35 - (BOOL)shouldShowMaster; |
|
36 - (NSString *)nameOfInterfaceOrientation:(UIInterfaceOrientation)theOrientation; |
|
37 - (void)reconfigureForMasterInPopover:(BOOL)inPopover; |
|
38 |
|
39 @end |
|
40 |
|
41 |
|
42 @implementation MGSplitViewController |
|
43 |
|
44 |
|
45 #pragma mark - |
|
46 #pragma mark Orientation helpers |
|
47 |
|
48 |
|
49 - (NSString *)nameOfInterfaceOrientation:(UIInterfaceOrientation)theOrientation |
|
50 { |
|
51 NSString *orientationName = nil; |
|
52 switch (theOrientation) { |
|
53 case UIInterfaceOrientationPortrait: |
|
54 orientationName = @"Portrait"; // Home button at bottom |
|
55 break; |
|
56 case UIInterfaceOrientationPortraitUpsideDown: |
|
57 orientationName = @"Portrait (Upside Down)"; // Home button at top |
|
58 break; |
|
59 case UIInterfaceOrientationLandscapeLeft: |
|
60 orientationName = @"Landscape (Left)"; // Home button on left |
|
61 break; |
|
62 case UIInterfaceOrientationLandscapeRight: |
|
63 orientationName = @"Landscape (Right)"; // Home button on right |
|
64 break; |
|
65 default: |
|
66 break; |
|
67 } |
|
68 |
|
69 return orientationName; |
|
70 } |
|
71 |
|
72 |
|
73 - (BOOL)isLandscape |
|
74 { |
|
75 return UIInterfaceOrientationIsLandscape(self.interfaceOrientation); |
|
76 } |
|
77 |
|
78 |
|
79 - (BOOL)shouldShowMasterForInterfaceOrientation:(UIInterfaceOrientation)theOrientation |
|
80 { |
|
81 // Returns YES if master view should be shown directly embedded in the splitview, instead of hidden in a popover. |
|
82 return ((UIInterfaceOrientationIsLandscape(theOrientation)) ? _showsMasterInLandscape : _showsMasterInPortrait); |
|
83 } |
|
84 |
|
85 |
|
86 - (BOOL)shouldShowMaster |
|
87 { |
|
88 return [self shouldShowMasterForInterfaceOrientation:self.interfaceOrientation]; |
|
89 } |
|
90 |
|
91 |
|
92 - (BOOL)isShowingMaster |
|
93 { |
|
94 return [self shouldShowMaster] && self.masterViewController && self.masterViewController.view && ([self.masterViewController.view superview] == self.view); |
|
95 } |
|
96 |
|
97 |
|
98 #pragma mark - |
|
99 #pragma mark Setup and Teardown |
|
100 |
|
101 |
|
102 - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil |
|
103 { |
|
104 if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) { |
|
105 [self setup]; |
|
106 } |
|
107 |
|
108 return self; |
|
109 } |
|
110 |
|
111 |
|
112 - (id)initWithCoder:(NSCoder *)aDecoder |
|
113 { |
|
114 if ((self = [super initWithCoder:aDecoder])) { |
|
115 [self setup]; |
|
116 } |
|
117 |
|
118 return self; |
|
119 } |
|
120 |
|
121 |
|
122 - (void)setup |
|
123 { |
|
124 // Configure default behaviour. |
|
125 _viewControllers = [[NSMutableArray alloc] initWithObjects:[NSNull null], [NSNull null], nil]; |
|
126 _splitWidth = MG_DEFAULT_SPLIT_WIDTH; |
|
127 _showsMasterInPortrait = NO; |
|
128 _showsMasterInLandscape = YES; |
|
129 _reconfigurePopup = NO; |
|
130 _vertical = YES; |
|
131 _masterBeforeDetail = YES; |
|
132 _splitPosition = MG_DEFAULT_SPLIT_POSITION; |
|
133 CGRect divRect = self.view.bounds; |
|
134 if ([self isVertical]) { |
|
135 divRect.origin.y = _splitPosition; |
|
136 divRect.size.height = _splitWidth; |
|
137 } else { |
|
138 divRect.origin.x = _splitPosition; |
|
139 divRect.size.width = _splitWidth; |
|
140 } |
|
141 _dividerView = [[MGSplitDividerView alloc] initWithFrame:divRect]; |
|
142 _dividerView.splitViewController = self; |
|
143 _dividerView.backgroundColor = MG_DEFAULT_CORNER_COLOR; |
|
144 _dividerStyle = MGSplitViewDividerStyleThin; |
|
145 } |
|
146 |
|
147 |
|
148 - (void)dealloc |
|
149 { |
|
150 _delegate = nil; |
|
151 [self.view.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; |
|
152 [_viewControllers release]; |
|
153 [_barButtonItem release]; |
|
154 [_hiddenPopoverController release]; |
|
155 [_dividerView release]; |
|
156 [_cornerViews release]; |
|
157 |
|
158 [super dealloc]; |
|
159 } |
|
160 |
|
161 |
|
162 #pragma mark - |
|
163 #pragma mark View management |
|
164 |
|
165 |
|
166 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation |
|
167 { |
|
168 return YES; |
|
169 } |
|
170 |
|
171 |
|
172 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration |
|
173 { |
|
174 [self.masterViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
|
175 [self.detailViewController willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
|
176 } |
|
177 |
|
178 |
|
179 - (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation |
|
180 { |
|
181 [self.masterViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation]; |
|
182 [self.detailViewController didRotateFromInterfaceOrientation:fromInterfaceOrientation]; |
|
183 } |
|
184 |
|
185 |
|
186 - (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation |
|
187 duration:(NSTimeInterval)duration |
|
188 { |
|
189 [self.masterViewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
|
190 [self.detailViewController willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
|
191 |
|
192 // Hide popover. |
|
193 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
194 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
195 } |
|
196 |
|
197 // Re-tile views. |
|
198 _reconfigurePopup = YES; |
|
199 [self layoutSubviewsForInterfaceOrientation:toInterfaceOrientation withAnimation:YES]; |
|
200 } |
|
201 |
|
202 |
|
203 - (void)willAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration |
|
204 { |
|
205 [self.masterViewController willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
|
206 [self.detailViewController willAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation duration:duration]; |
|
207 } |
|
208 |
|
209 |
|
210 - (void)didAnimateFirstHalfOfRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation |
|
211 { |
|
212 [self.masterViewController didAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation]; |
|
213 [self.detailViewController didAnimateFirstHalfOfRotationToInterfaceOrientation:toInterfaceOrientation]; |
|
214 } |
|
215 |
|
216 |
|
217 - (void)willAnimateSecondHalfOfRotationFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation duration:(NSTimeInterval)duration |
|
218 { |
|
219 [self.masterViewController willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration]; |
|
220 [self.detailViewController willAnimateSecondHalfOfRotationFromInterfaceOrientation:fromInterfaceOrientation duration:duration]; |
|
221 } |
|
222 |
|
223 |
|
224 - (CGSize)splitViewSizeForOrientation:(UIInterfaceOrientation)theOrientation |
|
225 { |
|
226 UIScreen *screen = [UIScreen mainScreen]; |
|
227 CGRect fullScreenRect = screen.bounds; // always implicitly in Portrait orientation. |
|
228 CGRect appFrame = screen.applicationFrame; |
|
229 |
|
230 // Find status bar height by checking which dimension of the applicationFrame is narrower than screen bounds. |
|
231 // Little bit ugly looking, but it'll still work even if they change the status bar height in future. |
|
232 float statusBarHeight = MAX((fullScreenRect.size.width - appFrame.size.width), (fullScreenRect.size.height - appFrame.size.height)); |
|
233 |
|
234 // Initially assume portrait orientation. |
|
235 float width = fullScreenRect.size.width; |
|
236 float height = fullScreenRect.size.height; |
|
237 |
|
238 // Correct for orientation. |
|
239 if (UIInterfaceOrientationIsLandscape(theOrientation)) { |
|
240 width = height; |
|
241 height = fullScreenRect.size.width; |
|
242 } |
|
243 |
|
244 // Account for status bar, which always subtracts from the height (since it's always at the top of the screen). |
|
245 height -= statusBarHeight; |
|
246 |
|
247 return CGSizeMake(width, height); |
|
248 } |
|
249 |
|
250 |
|
251 - (void)layoutSubviewsForInterfaceOrientation:(UIInterfaceOrientation)theOrientation withAnimation:(BOOL)animate |
|
252 { |
|
253 if (_reconfigurePopup) { |
|
254 [self reconfigureForMasterInPopover:![self shouldShowMasterForInterfaceOrientation:theOrientation]]; |
|
255 } |
|
256 |
|
257 // Layout the master, detail and divider views appropriately, adding/removing subviews as needed. |
|
258 // First obtain relevant geometry. |
|
259 CGSize fullSize = [self splitViewSizeForOrientation:theOrientation]; |
|
260 float width = fullSize.width; |
|
261 float height = fullSize.height; |
|
262 |
|
263 if (NO) { // Just for debugging. |
|
264 NSLog(@"Target orientation is %@, dimensions will be %.0f x %.0f", |
|
265 [self nameOfInterfaceOrientation:theOrientation], width, height); |
|
266 } |
|
267 |
|
268 // Layout the master, divider and detail views. |
|
269 CGRect newFrame = CGRectMake(0, 0, width, height); |
|
270 UIViewController *controller; |
|
271 UIView *theView; |
|
272 BOOL shouldShowMaster = [self shouldShowMasterForInterfaceOrientation:theOrientation]; |
|
273 BOOL masterFirst = [self isMasterBeforeDetail]; |
|
274 if ([self isVertical]) { |
|
275 // Master on left, detail on right (or vice versa). |
|
276 CGRect masterRect, dividerRect, detailRect; |
|
277 if (masterFirst) { |
|
278 if (!shouldShowMaster) { |
|
279 // Move off-screen. |
|
280 newFrame.origin.x -= (_splitPosition + _splitWidth); |
|
281 } |
|
282 |
|
283 newFrame.size.width = _splitPosition; |
|
284 masterRect = newFrame; |
|
285 |
|
286 newFrame.origin.x += newFrame.size.width; |
|
287 newFrame.size.width = _splitWidth; |
|
288 dividerRect = newFrame; |
|
289 |
|
290 newFrame.origin.x += newFrame.size.width; |
|
291 newFrame.size.width = width - newFrame.origin.x; |
|
292 detailRect = newFrame; |
|
293 |
|
294 } else { |
|
295 if (!shouldShowMaster) { |
|
296 // Move off-screen. |
|
297 newFrame.size.width += (_splitPosition + _splitWidth); |
|
298 } |
|
299 |
|
300 newFrame.size.width -= (_splitPosition + _splitWidth); |
|
301 detailRect = newFrame; |
|
302 |
|
303 newFrame.origin.x += newFrame.size.width; |
|
304 newFrame.size.width = _splitWidth; |
|
305 dividerRect = newFrame; |
|
306 |
|
307 newFrame.origin.x += newFrame.size.width; |
|
308 newFrame.size.width = _splitPosition; |
|
309 masterRect = newFrame; |
|
310 } |
|
311 |
|
312 // Position master. |
|
313 controller = self.masterViewController; |
|
314 if (controller && [controller isKindOfClass:[UIViewController class]]) { |
|
315 theView = controller.view; |
|
316 if (theView) { |
|
317 theView.frame = masterRect; |
|
318 if (!theView.superview) { |
|
319 [controller viewWillAppear:NO]; |
|
320 [self.view addSubview:theView]; |
|
321 [controller viewDidAppear:NO]; |
|
322 } |
|
323 } |
|
324 } |
|
325 |
|
326 // Position divider. |
|
327 theView = _dividerView; |
|
328 theView.frame = dividerRect; |
|
329 if (!theView.superview) { |
|
330 [self.view addSubview:theView]; |
|
331 } |
|
332 |
|
333 // Position detail. |
|
334 controller = self.detailViewController; |
|
335 if (controller && [controller isKindOfClass:[UIViewController class]]) { |
|
336 theView = controller.view; |
|
337 if (theView) { |
|
338 theView.frame = detailRect; |
|
339 if (!theView.superview) { |
|
340 [self.view insertSubview:theView aboveSubview:self.masterViewController.view]; |
|
341 } else { |
|
342 [self.view bringSubviewToFront:theView]; |
|
343 } |
|
344 } |
|
345 } |
|
346 |
|
347 } else { |
|
348 // Master above, detail below (or vice versa). |
|
349 CGRect masterRect, dividerRect, detailRect; |
|
350 if (masterFirst) { |
|
351 if (!shouldShowMaster) { |
|
352 // Move off-screen. |
|
353 newFrame.origin.y -= (_splitPosition + _splitWidth); |
|
354 } |
|
355 |
|
356 newFrame.size.height = _splitPosition; |
|
357 masterRect = newFrame; |
|
358 |
|
359 newFrame.origin.y += newFrame.size.height; |
|
360 newFrame.size.height = _splitWidth; |
|
361 dividerRect = newFrame; |
|
362 |
|
363 newFrame.origin.y += newFrame.size.height; |
|
364 newFrame.size.height = height - newFrame.origin.y; |
|
365 detailRect = newFrame; |
|
366 |
|
367 } else { |
|
368 if (!shouldShowMaster) { |
|
369 // Move off-screen. |
|
370 newFrame.size.height += (_splitPosition + _splitWidth); |
|
371 } |
|
372 |
|
373 newFrame.size.height -= (_splitPosition + _splitWidth); |
|
374 detailRect = newFrame; |
|
375 |
|
376 newFrame.origin.y += newFrame.size.height; |
|
377 newFrame.size.height = _splitWidth; |
|
378 dividerRect = newFrame; |
|
379 |
|
380 newFrame.origin.y += newFrame.size.height; |
|
381 newFrame.size.height = _splitPosition; |
|
382 masterRect = newFrame; |
|
383 } |
|
384 |
|
385 // Position master. |
|
386 controller = self.masterViewController; |
|
387 if (controller && [controller isKindOfClass:[UIViewController class]]) { |
|
388 theView = controller.view; |
|
389 if (theView) { |
|
390 theView.frame = masterRect; |
|
391 if (!theView.superview) { |
|
392 [controller viewWillAppear:NO]; |
|
393 [self.view addSubview:theView]; |
|
394 [controller viewDidAppear:NO]; |
|
395 } |
|
396 } |
|
397 } |
|
398 |
|
399 // Position divider. |
|
400 theView = _dividerView; |
|
401 theView.frame = dividerRect; |
|
402 if (!theView.superview) { |
|
403 [self.view addSubview:theView]; |
|
404 } |
|
405 |
|
406 // Position detail. |
|
407 controller = self.detailViewController; |
|
408 if (controller && [controller isKindOfClass:[UIViewController class]]) { |
|
409 theView = controller.view; |
|
410 if (theView) { |
|
411 theView.frame = detailRect; |
|
412 if (!theView.superview) { |
|
413 [self.view insertSubview:theView aboveSubview:self.masterViewController.view]; |
|
414 } else { |
|
415 [self.view bringSubviewToFront:theView]; |
|
416 } |
|
417 } |
|
418 } |
|
419 } |
|
420 |
|
421 // Create corner views if necessary. |
|
422 MGSplitCornersView *leadingCorners; // top/left of screen in vertical/horizontal split. |
|
423 MGSplitCornersView *trailingCorners; // bottom/right of screen in vertical/horizontal split. |
|
424 if (!_cornerViews) { |
|
425 CGRect cornerRect = CGRectMake(0, 0, 10, 10); // arbitrary, will be resized below. |
|
426 leadingCorners = [[MGSplitCornersView alloc] initWithFrame:cornerRect]; |
|
427 leadingCorners.splitViewController = self; |
|
428 leadingCorners.cornerBackgroundColor = MG_DEFAULT_CORNER_COLOR; |
|
429 leadingCorners.cornerRadius = MG_DEFAULT_CORNER_RADIUS; |
|
430 trailingCorners = [[MGSplitCornersView alloc] initWithFrame:cornerRect]; |
|
431 trailingCorners.splitViewController = self; |
|
432 trailingCorners.cornerBackgroundColor = MG_DEFAULT_CORNER_COLOR; |
|
433 trailingCorners.cornerRadius = MG_DEFAULT_CORNER_RADIUS; |
|
434 _cornerViews = [[NSArray alloc] initWithObjects:leadingCorners, trailingCorners, nil]; |
|
435 [leadingCorners release]; |
|
436 [trailingCorners release]; |
|
437 |
|
438 } else if ([_cornerViews count] == 2) { |
|
439 leadingCorners = [_cornerViews objectAtIndex:0]; |
|
440 trailingCorners = [_cornerViews objectAtIndex:1]; |
|
441 } |
|
442 |
|
443 // Configure and layout the corner-views. |
|
444 leadingCorners.cornersPosition = (_vertical) ? MGCornersPositionLeadingVertical : MGCornersPositionLeadingHorizontal; |
|
445 trailingCorners.cornersPosition = (_vertical) ? MGCornersPositionTrailingVertical : MGCornersPositionTrailingHorizontal; |
|
446 leadingCorners.autoresizingMask = (_vertical) ? UIViewAutoresizingFlexibleBottomMargin : UIViewAutoresizingFlexibleRightMargin; |
|
447 trailingCorners.autoresizingMask = (_vertical) ? UIViewAutoresizingFlexibleTopMargin : UIViewAutoresizingFlexibleLeftMargin; |
|
448 |
|
449 float x, y, cornersWidth, cornersHeight; |
|
450 CGRect leadingRect, trailingRect; |
|
451 float radius = leadingCorners.cornerRadius; |
|
452 if (_vertical) { // left/right split |
|
453 cornersWidth = (radius * 2.0) + _splitWidth; |
|
454 cornersHeight = radius; |
|
455 x = ((shouldShowMaster) ? ((masterFirst) ? _splitPosition : width - (_splitPosition + _splitWidth)) : (0 - _splitWidth)) - radius; |
|
456 y = 0; |
|
457 leadingRect = CGRectMake(x, y, cornersWidth, cornersHeight); // top corners |
|
458 trailingRect = CGRectMake(x, (height - cornersHeight), cornersWidth, cornersHeight); // bottom corners |
|
459 |
|
460 } else { // top/bottom split |
|
461 x = 0; |
|
462 y = ((shouldShowMaster) ? ((masterFirst) ? _splitPosition : height - (_splitPosition + _splitWidth)) : (0 - _splitWidth)) - radius; |
|
463 cornersWidth = radius; |
|
464 cornersHeight = (radius * 2.0) + _splitWidth; |
|
465 leadingRect = CGRectMake(x, y, cornersWidth, cornersHeight); // left corners |
|
466 trailingRect = CGRectMake((width - cornersWidth), y, cornersWidth, cornersHeight); // right corners |
|
467 } |
|
468 |
|
469 leadingCorners.frame = leadingRect; |
|
470 trailingCorners.frame = trailingRect; |
|
471 |
|
472 // Ensure corners are visible and frontmost. |
|
473 if (!leadingCorners.superview) { |
|
474 [self.view insertSubview:leadingCorners aboveSubview:self.detailViewController.view]; |
|
475 [self.view insertSubview:trailingCorners aboveSubview:self.detailViewController.view]; |
|
476 } else { |
|
477 [self.view bringSubviewToFront:leadingCorners]; |
|
478 [self.view bringSubviewToFront:trailingCorners]; |
|
479 } |
|
480 } |
|
481 |
|
482 |
|
483 - (void)layoutSubviewsWithAnimation:(BOOL)animate |
|
484 { |
|
485 [self layoutSubviewsForInterfaceOrientation:self.interfaceOrientation withAnimation:animate]; |
|
486 } |
|
487 |
|
488 |
|
489 - (void)layoutSubviews |
|
490 { |
|
491 [self layoutSubviewsForInterfaceOrientation:self.interfaceOrientation withAnimation:YES]; |
|
492 } |
|
493 |
|
494 |
|
495 - (void)viewWillAppear:(BOOL)animated |
|
496 { |
|
497 [super viewWillAppear:animated]; |
|
498 |
|
499 if ([self isShowingMaster]) { |
|
500 [self.masterViewController viewWillAppear:animated]; |
|
501 } |
|
502 [self.detailViewController viewWillAppear:animated]; |
|
503 |
|
504 _reconfigurePopup = YES; |
|
505 [self layoutSubviews]; |
|
506 } |
|
507 |
|
508 |
|
509 - (void)viewDidAppear:(BOOL)animated |
|
510 { |
|
511 [super viewDidAppear:animated]; |
|
512 |
|
513 if ([self isShowingMaster]) { |
|
514 [self.masterViewController viewDidAppear:animated]; |
|
515 } |
|
516 [self.detailViewController viewDidAppear:animated]; |
|
517 } |
|
518 |
|
519 |
|
520 - (void)viewWillDisappear:(BOOL)animated |
|
521 { |
|
522 [super viewWillDisappear:animated]; |
|
523 |
|
524 if ([self isShowingMaster]) { |
|
525 [self.masterViewController viewWillDisappear:animated]; |
|
526 } |
|
527 [self.detailViewController viewWillDisappear:animated]; |
|
528 } |
|
529 |
|
530 |
|
531 - (void)viewDidDisappear:(BOOL)animated |
|
532 { |
|
533 [super viewDidDisappear:animated]; |
|
534 |
|
535 if ([self isShowingMaster]) { |
|
536 [self.masterViewController viewDidDisappear:animated]; |
|
537 } |
|
538 [self.detailViewController viewDidDisappear:animated]; |
|
539 } |
|
540 |
|
541 |
|
542 #pragma mark - |
|
543 #pragma mark Popover handling |
|
544 |
|
545 |
|
546 - (void)reconfigureForMasterInPopover:(BOOL)inPopover |
|
547 { |
|
548 _reconfigurePopup = NO; |
|
549 |
|
550 if ((inPopover && _hiddenPopoverController) || (!inPopover && !_hiddenPopoverController) || !self.masterViewController) { |
|
551 // Nothing to do. |
|
552 return; |
|
553 } |
|
554 |
|
555 if (inPopover && !_hiddenPopoverController && !_barButtonItem) { |
|
556 // Create and configure popover for our masterViewController. |
|
557 [_hiddenPopoverController release]; |
|
558 _hiddenPopoverController = nil; |
|
559 [self.masterViewController viewWillDisappear:NO]; |
|
560 _hiddenPopoverController = [[UIPopoverController alloc] initWithContentViewController:self.masterViewController]; |
|
561 [self.masterViewController viewDidDisappear:NO]; |
|
562 |
|
563 // Create and configure _barButtonItem. |
|
564 _barButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Master", nil) |
|
565 style:UIBarButtonItemStyleBordered |
|
566 target:self |
|
567 action:@selector(showMasterPopover:)]; |
|
568 |
|
569 // Inform delegate of this state of affairs. |
|
570 if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willHideViewController:withBarButtonItem:forPopoverController:)]) { |
|
571 [(NSObject <MGSplitViewControllerDelegate> *)_delegate splitViewController:self |
|
572 willHideViewController:self.masterViewController |
|
573 withBarButtonItem:_barButtonItem |
|
574 forPopoverController:_hiddenPopoverController]; |
|
575 } |
|
576 |
|
577 } else if (!inPopover && _hiddenPopoverController && _barButtonItem) { |
|
578 // I know this looks strange, but it fixes a bizarre issue with UIPopoverController leaving masterViewController's views in disarray. |
|
579 [_hiddenPopoverController presentPopoverFromRect:CGRectZero inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:NO]; |
|
580 |
|
581 // Remove master from popover and destroy popover, if it exists. |
|
582 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
583 [_hiddenPopoverController release]; |
|
584 _hiddenPopoverController = nil; |
|
585 |
|
586 // Inform delegate that the _barButtonItem will become invalid. |
|
587 if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willShowViewController:invalidatingBarButtonItem:)]) { |
|
588 [(NSObject <MGSplitViewControllerDelegate> *)_delegate splitViewController:self |
|
589 willShowViewController:self.masterViewController |
|
590 invalidatingBarButtonItem:_barButtonItem]; |
|
591 } |
|
592 |
|
593 // Destroy _barButtonItem. |
|
594 [_barButtonItem release]; |
|
595 _barButtonItem = nil; |
|
596 |
|
597 // Move master view. |
|
598 UIView *masterView = self.masterViewController.view; |
|
599 if (masterView && masterView.superview != self.view) { |
|
600 [masterView removeFromSuperview]; |
|
601 } |
|
602 } |
|
603 } |
|
604 |
|
605 |
|
606 - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController |
|
607 { |
|
608 [self reconfigureForMasterInPopover:NO]; |
|
609 } |
|
610 |
|
611 |
|
612 - (void)notePopoverDismissed |
|
613 { |
|
614 [self popoverControllerDidDismissPopover:_hiddenPopoverController]; |
|
615 } |
|
616 |
|
617 |
|
618 #pragma mark - |
|
619 #pragma mark Animations |
|
620 |
|
621 |
|
622 - (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context |
|
623 { |
|
624 if (([animationID isEqualToString:MG_ANIMATION_CHANGE_SPLIT_ORIENTATION] || |
|
625 [animationID isEqualToString:MG_ANIMATION_CHANGE_SUBVIEWS_ORDER]) |
|
626 && _cornerViews) { |
|
627 for (UIView *corner in _cornerViews) { |
|
628 corner.hidden = NO; |
|
629 } |
|
630 _dividerView.hidden = NO; |
|
631 } |
|
632 } |
|
633 |
|
634 |
|
635 #pragma mark - |
|
636 #pragma mark IB Actions |
|
637 |
|
638 |
|
639 - (IBAction)toggleSplitOrientation:(id)sender |
|
640 { |
|
641 BOOL showingMaster = [self isShowingMaster]; |
|
642 if (showingMaster) { |
|
643 if (_cornerViews) { |
|
644 for (UIView *corner in _cornerViews) { |
|
645 corner.hidden = YES; |
|
646 } |
|
647 _dividerView.hidden = YES; |
|
648 } |
|
649 [UIView beginAnimations:MG_ANIMATION_CHANGE_SPLIT_ORIENTATION context:nil]; |
|
650 [UIView setAnimationDelegate:self]; |
|
651 [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)]; |
|
652 } |
|
653 self.vertical = (!self.vertical); |
|
654 if (showingMaster) { |
|
655 [UIView commitAnimations]; |
|
656 } |
|
657 } |
|
658 |
|
659 |
|
660 - (IBAction)toggleMasterBeforeDetail:(id)sender |
|
661 { |
|
662 BOOL showingMaster = [self isShowingMaster]; |
|
663 if (showingMaster) { |
|
664 if (_cornerViews) { |
|
665 for (UIView *corner in _cornerViews) { |
|
666 corner.hidden = YES; |
|
667 } |
|
668 _dividerView.hidden = YES; |
|
669 } |
|
670 [UIView beginAnimations:MG_ANIMATION_CHANGE_SUBVIEWS_ORDER context:nil]; |
|
671 [UIView setAnimationDelegate:self]; |
|
672 [UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)]; |
|
673 } |
|
674 self.masterBeforeDetail = (!self.masterBeforeDetail); |
|
675 if (showingMaster) { |
|
676 [UIView commitAnimations]; |
|
677 } |
|
678 } |
|
679 |
|
680 |
|
681 - (IBAction)toggleMasterView:(id)sender |
|
682 { |
|
683 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
684 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
685 } |
|
686 |
|
687 if (![self isShowingMaster]) { |
|
688 // We're about to show the master view. Ensure it's in place off-screen to be animated in. |
|
689 _reconfigurePopup = YES; |
|
690 [self reconfigureForMasterInPopover:NO]; |
|
691 [self layoutSubviews]; |
|
692 } |
|
693 |
|
694 // This action functions on the current primary orientation; it is independent of the other primary orientation. |
|
695 [UIView beginAnimations:@"toggleMaster" context:nil]; |
|
696 if (self.isLandscape) { |
|
697 self.showsMasterInLandscape = !_showsMasterInLandscape; |
|
698 } else { |
|
699 self.showsMasterInPortrait = !_showsMasterInPortrait; |
|
700 } |
|
701 [UIView commitAnimations]; |
|
702 } |
|
703 |
|
704 |
|
705 - (IBAction)showMasterPopover:(id)sender |
|
706 { |
|
707 if (_hiddenPopoverController && !(_hiddenPopoverController.popoverVisible)) { |
|
708 // Inform delegate. |
|
709 if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:popoverController:willPresentViewController:)]) { |
|
710 [(NSObject <MGSplitViewControllerDelegate> *)_delegate splitViewController:self |
|
711 popoverController:_hiddenPopoverController |
|
712 willPresentViewController:self.masterViewController]; |
|
713 } |
|
714 |
|
715 // Show popover. |
|
716 [_hiddenPopoverController presentPopoverFromBarButtonItem:_barButtonItem permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; |
|
717 } |
|
718 } |
|
719 |
|
720 |
|
721 #pragma mark - |
|
722 #pragma mark Accessors and properties |
|
723 |
|
724 |
|
725 - (id)delegate |
|
726 { |
|
727 return _delegate; |
|
728 } |
|
729 |
|
730 |
|
731 - (void)setDelegate:(id <MGSplitViewControllerDelegate>)newDelegate |
|
732 { |
|
733 if (newDelegate != _delegate && |
|
734 (!newDelegate || [(NSObject *)newDelegate conformsToProtocol:@protocol(MGSplitViewControllerDelegate)])) { |
|
735 _delegate = newDelegate; |
|
736 } |
|
737 } |
|
738 |
|
739 |
|
740 - (BOOL)showsMasterInPortrait |
|
741 { |
|
742 return _showsMasterInPortrait; |
|
743 } |
|
744 |
|
745 |
|
746 - (void)setShowsMasterInPortrait:(BOOL)flag |
|
747 { |
|
748 if (flag != _showsMasterInPortrait) { |
|
749 _showsMasterInPortrait = flag; |
|
750 |
|
751 if (![self isLandscape]) { // i.e. if this will cause a visual change. |
|
752 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
753 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
754 } |
|
755 |
|
756 // Rearrange views. |
|
757 _reconfigurePopup = YES; |
|
758 [self layoutSubviews]; |
|
759 } |
|
760 } |
|
761 } |
|
762 |
|
763 |
|
764 - (BOOL)showsMasterInLandscape |
|
765 { |
|
766 return _showsMasterInLandscape; |
|
767 } |
|
768 |
|
769 |
|
770 - (void)setShowsMasterInLandscape:(BOOL)flag |
|
771 { |
|
772 if (flag != _showsMasterInLandscape) { |
|
773 _showsMasterInLandscape = flag; |
|
774 |
|
775 if ([self isLandscape]) { // i.e. if this will cause a visual change. |
|
776 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
777 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
778 } |
|
779 |
|
780 // Rearrange views. |
|
781 _reconfigurePopup = YES; |
|
782 [self layoutSubviews]; |
|
783 } |
|
784 } |
|
785 } |
|
786 |
|
787 |
|
788 - (BOOL)isVertical |
|
789 { |
|
790 return _vertical; |
|
791 } |
|
792 |
|
793 |
|
794 - (void)setVertical:(BOOL)flag |
|
795 { |
|
796 if (flag != _vertical) { |
|
797 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
798 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
799 } |
|
800 |
|
801 _vertical = flag; |
|
802 |
|
803 // Inform delegate. |
|
804 if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willChangeSplitOrientationToVertical:)]) { |
|
805 [_delegate splitViewController:self willChangeSplitOrientationToVertical:_vertical]; |
|
806 } |
|
807 |
|
808 [self layoutSubviews]; |
|
809 } |
|
810 } |
|
811 |
|
812 |
|
813 - (BOOL)isMasterBeforeDetail |
|
814 { |
|
815 return _masterBeforeDetail; |
|
816 } |
|
817 |
|
818 |
|
819 - (void)setMasterBeforeDetail:(BOOL)flag |
|
820 { |
|
821 if (flag != _masterBeforeDetail) { |
|
822 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
823 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
824 } |
|
825 |
|
826 _masterBeforeDetail = flag; |
|
827 |
|
828 if ([self isShowingMaster]) { |
|
829 [self layoutSubviews]; |
|
830 } |
|
831 } |
|
832 } |
|
833 |
|
834 |
|
835 - (float)splitPosition |
|
836 { |
|
837 return _splitPosition; |
|
838 } |
|
839 |
|
840 |
|
841 - (void)setSplitPosition:(float)posn |
|
842 { |
|
843 // Check to see if delegate wishes to constrain the position. |
|
844 float newPosn = posn; |
|
845 BOOL constrained = NO; |
|
846 CGSize fullSize = [self splitViewSizeForOrientation:self.interfaceOrientation]; |
|
847 if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:constrainSplitPosition:splitViewSize:)]) { |
|
848 newPosn = [_delegate splitViewController:self constrainSplitPosition:newPosn splitViewSize:fullSize]; |
|
849 constrained = YES; // implicitly trust delegate's response. |
|
850 |
|
851 } else { |
|
852 // Apply default constraints if delegate doesn't wish to participate. |
|
853 float minPos = MG_MIN_VIEW_WIDTH; |
|
854 float maxPos = ((_vertical) ? fullSize.width : fullSize.height) - (MG_MIN_VIEW_WIDTH + _splitWidth); |
|
855 constrained = (newPosn != _splitPosition && newPosn >= minPos && newPosn <= maxPos); |
|
856 } |
|
857 |
|
858 if (constrained) { |
|
859 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
860 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
861 } |
|
862 |
|
863 _splitPosition = newPosn; |
|
864 |
|
865 // Inform delegate. |
|
866 if (_delegate && [_delegate respondsToSelector:@selector(splitViewController:willMoveSplitToPosition:)]) { |
|
867 [_delegate splitViewController:self willMoveSplitToPosition:_splitPosition]; |
|
868 } |
|
869 |
|
870 if ([self isShowingMaster]) { |
|
871 [self layoutSubviews]; |
|
872 } |
|
873 } |
|
874 } |
|
875 |
|
876 |
|
877 - (void)setSplitPosition:(float)posn animated:(BOOL)animate |
|
878 { |
|
879 BOOL shouldAnimate = (animate && [self isShowingMaster]); |
|
880 if (shouldAnimate) { |
|
881 [UIView beginAnimations:@"SplitPosition" context:nil]; |
|
882 } |
|
883 [self setSplitPosition:posn]; |
|
884 if (shouldAnimate) { |
|
885 [UIView commitAnimations]; |
|
886 } |
|
887 } |
|
888 |
|
889 |
|
890 - (float)splitWidth |
|
891 { |
|
892 return _splitWidth; |
|
893 } |
|
894 |
|
895 |
|
896 - (void)setSplitWidth:(float)width |
|
897 { |
|
898 if (width != _splitWidth && width >= 0) { |
|
899 _splitWidth = width; |
|
900 if ([self isShowingMaster]) { |
|
901 [self layoutSubviews]; |
|
902 } |
|
903 } |
|
904 } |
|
905 |
|
906 |
|
907 - (NSArray *)viewControllers |
|
908 { |
|
909 return [[_viewControllers copy] autorelease]; |
|
910 } |
|
911 |
|
912 |
|
913 - (void)setViewControllers:(NSArray *)controllers |
|
914 { |
|
915 if (controllers != _viewControllers) { |
|
916 for (UIViewController *controller in _viewControllers) { |
|
917 if ([controller isKindOfClass:[UIViewController class]]) { |
|
918 [controller.view removeFromSuperview]; |
|
919 } |
|
920 } |
|
921 [_viewControllers release]; |
|
922 _viewControllers = [[NSMutableArray alloc] initWithCapacity:2]; |
|
923 if (controllers && [controllers count] >= 2) { |
|
924 self.masterViewController = [controllers objectAtIndex:0]; |
|
925 self.detailViewController = [controllers objectAtIndex:1]; |
|
926 } else { |
|
927 NSLog(@"Error: %@ requires 2 view-controllers. (%@)", NSStringFromClass([self class]), NSStringFromSelector(_cmd)); |
|
928 } |
|
929 |
|
930 [self layoutSubviews]; |
|
931 } |
|
932 } |
|
933 |
|
934 |
|
935 - (UIViewController *)masterViewController |
|
936 { |
|
937 if (_viewControllers && [_viewControllers count] > 0) { |
|
938 NSObject *controller = [_viewControllers objectAtIndex:0]; |
|
939 if ([controller isKindOfClass:[UIViewController class]]) { |
|
940 return [[controller retain] autorelease]; |
|
941 } |
|
942 } |
|
943 |
|
944 return nil; |
|
945 } |
|
946 |
|
947 |
|
948 - (void)setMasterViewController:(UIViewController *)master |
|
949 { |
|
950 if (!_viewControllers) { |
|
951 _viewControllers = [[NSMutableArray alloc] initWithCapacity:2]; |
|
952 } |
|
953 |
|
954 NSObject *newMaster = master; |
|
955 if (!newMaster) { |
|
956 newMaster = [NSNull null]; |
|
957 } |
|
958 |
|
959 BOOL changed = YES; |
|
960 if ([_viewControllers count] > 0) { |
|
961 if ([_viewControllers objectAtIndex:0] == newMaster) { |
|
962 changed = NO; |
|
963 } else { |
|
964 [_viewControllers replaceObjectAtIndex:0 withObject:newMaster]; |
|
965 } |
|
966 |
|
967 } else { |
|
968 [_viewControllers addObject:newMaster]; |
|
969 } |
|
970 |
|
971 if (changed) { |
|
972 [self layoutSubviews]; |
|
973 } |
|
974 } |
|
975 |
|
976 |
|
977 - (UIViewController *)detailViewController |
|
978 { |
|
979 if (_viewControllers && [_viewControllers count] > 1) { |
|
980 NSObject *controller = [_viewControllers objectAtIndex:1]; |
|
981 if ([controller isKindOfClass:[UIViewController class]]) { |
|
982 return [[controller retain] autorelease]; |
|
983 } |
|
984 } |
|
985 |
|
986 return nil; |
|
987 } |
|
988 |
|
989 |
|
990 - (void)setDetailViewController:(UIViewController *)detail |
|
991 { |
|
992 if (!_viewControllers) { |
|
993 _viewControllers = [[NSMutableArray alloc] initWithCapacity:2]; |
|
994 [_viewControllers addObject:[NSNull null]]; |
|
995 } |
|
996 |
|
997 BOOL changed = YES; |
|
998 if ([_viewControllers count] > 1) { |
|
999 if ([_viewControllers objectAtIndex:1] == detail) { |
|
1000 changed = NO; |
|
1001 } else { |
|
1002 [_viewControllers replaceObjectAtIndex:1 withObject:detail]; |
|
1003 } |
|
1004 |
|
1005 } else { |
|
1006 [_viewControllers addObject:detail]; |
|
1007 } |
|
1008 |
|
1009 if (changed) { |
|
1010 [self layoutSubviews]; |
|
1011 } |
|
1012 } |
|
1013 |
|
1014 |
|
1015 - (MGSplitDividerView *)dividerView |
|
1016 { |
|
1017 return [[_dividerView retain] autorelease]; |
|
1018 } |
|
1019 |
|
1020 |
|
1021 - (void)setDividerView:(MGSplitDividerView *)divider |
|
1022 { |
|
1023 if (divider != _dividerView) { |
|
1024 [_dividerView removeFromSuperview]; |
|
1025 [_dividerView release]; |
|
1026 _dividerView = [divider retain]; |
|
1027 _dividerView.splitViewController = self; |
|
1028 _dividerView.backgroundColor = MG_DEFAULT_CORNER_COLOR; |
|
1029 if ([self isShowingMaster]) { |
|
1030 [self layoutSubviews]; |
|
1031 } |
|
1032 } |
|
1033 } |
|
1034 |
|
1035 |
|
1036 - (BOOL)allowsDraggingDivider |
|
1037 { |
|
1038 if (_dividerView) { |
|
1039 return _dividerView.allowsDragging; |
|
1040 } |
|
1041 |
|
1042 return NO; |
|
1043 } |
|
1044 |
|
1045 |
|
1046 - (void)setAllowsDraggingDivider:(BOOL)flag |
|
1047 { |
|
1048 if (self.allowsDraggingDivider != flag && _dividerView) { |
|
1049 _dividerView.allowsDragging = flag; |
|
1050 } |
|
1051 } |
|
1052 |
|
1053 |
|
1054 - (MGSplitViewDividerStyle)dividerStyle |
|
1055 { |
|
1056 return _dividerStyle; |
|
1057 } |
|
1058 |
|
1059 |
|
1060 - (void)setDividerStyle:(MGSplitViewDividerStyle)newStyle |
|
1061 { |
|
1062 if (_hiddenPopoverController && _hiddenPopoverController.popoverVisible) { |
|
1063 [_hiddenPopoverController dismissPopoverAnimated:NO]; |
|
1064 } |
|
1065 |
|
1066 // We don't check to see if newStyle equals _dividerStyle, because it's a meta-setting. |
|
1067 // Aspects could have been changed since it was set. |
|
1068 _dividerStyle = newStyle; |
|
1069 |
|
1070 // Reconfigure general appearance and behaviour. |
|
1071 float cornerRadius; |
|
1072 if (_dividerStyle == MGSplitViewDividerStyleThin) { |
|
1073 cornerRadius = MG_DEFAULT_CORNER_RADIUS; |
|
1074 _splitWidth = MG_DEFAULT_SPLIT_WIDTH; |
|
1075 self.allowsDraggingDivider = NO; |
|
1076 |
|
1077 } else if (_dividerStyle == MGSplitViewDividerStylePaneSplitter) { |
|
1078 cornerRadius = MG_PANESPLITTER_CORNER_RADIUS; |
|
1079 _splitWidth = MG_PANESPLITTER_SPLIT_WIDTH; |
|
1080 self.allowsDraggingDivider = YES; |
|
1081 } |
|
1082 |
|
1083 // Update divider and corners. |
|
1084 [_dividerView setNeedsDisplay]; |
|
1085 if (_cornerViews) { |
|
1086 for (MGSplitCornersView *corner in _cornerViews) { |
|
1087 corner.cornerRadius = cornerRadius; |
|
1088 } |
|
1089 } |
|
1090 |
|
1091 // Layout all views. |
|
1092 [self layoutSubviews]; |
|
1093 } |
|
1094 |
|
1095 |
|
1096 - (void)setDividerStyle:(MGSplitViewDividerStyle)newStyle animated:(BOOL)animate |
|
1097 { |
|
1098 BOOL shouldAnimate = (animate && [self isShowingMaster]); |
|
1099 if (shouldAnimate) { |
|
1100 [UIView beginAnimations:@"DividerStyle" context:nil]; |
|
1101 } |
|
1102 [self setDividerStyle:newStyle]; |
|
1103 if (shouldAnimate) { |
|
1104 [UIView commitAnimations]; |
|
1105 } |
|
1106 } |
|
1107 |
|
1108 |
|
1109 - (NSArray *)cornerViews |
|
1110 { |
|
1111 if (_cornerViews) { |
|
1112 return [[_cornerViews retain] autorelease]; |
|
1113 } |
|
1114 |
|
1115 return nil; |
|
1116 } |
|
1117 |
|
1118 |
|
1119 @synthesize showsMasterInPortrait; |
|
1120 @synthesize showsMasterInLandscape; |
|
1121 @synthesize vertical; |
|
1122 @synthesize delegate; |
|
1123 @synthesize viewControllers; |
|
1124 @synthesize masterViewController; |
|
1125 @synthesize detailViewController; |
|
1126 @synthesize dividerView; |
|
1127 @synthesize splitPosition; |
|
1128 @synthesize splitWidth; |
|
1129 @synthesize allowsDraggingDivider; |
|
1130 @synthesize dividerStyle; |
|
1131 |
|
1132 |
|
1133 @end |