ใน iPad มี UI แบบใหม่ๆ ที่เราไม่เคยได้ใช้ใน iPhone เพิ่มเข้ามาด้วย หลักๆ ที่เรามักเห็นในโปรแกรมทั่วไปก็คือ Popover และ Split View
Split View ช่วยให้เราแบ่งหน้าจอการทำงานออกเป็น 2 ข้าง โดย panel ทางด้านซ้ายจะมีความกว้างตายตัวคือ 320 pixels ส่วน panel ทางขวา นั้นจะกว้างตามขนาดของหน้าจอที่เหลือ โดยทั่วไปเมื่อหน้าจอเป็น landscape แล้ว split view จะแสดงทั้ง 2 pane (ต่อไปจะเรียก pane ทางซ้ายว่า Master pane และเรียก pane ทางขวาว่า Detail pane) แต่หากเรา rotate หน้าจอเป็น portrait แล้ว master pane ก็จะหายไปเหลือแต่ detail pane ที่ยังอยู่
โดยทั่วไป หากเราต้องการใช้งาน master pane ที่หายไปใน portrait mode จะใช้้วิธีเพิ่มปุ่มลงไปที่ toolbar/navigation bar ของ detail pane และเมื่อกดปุ่มนั้นแล้ว pane ทางซ้ายจะถูกแสดงขึ้นมาใน Popover view ดังรูปด้านล่าง
ลองใช้ Split View กันดีกว่า
หลักการก็คือ สร้าง split view ขึ้นมาจาก UISplitViewController จากนั้นสร้าง view controller อีก 2 ตัว และนำมากำหนดค่าให้กับ viewControllers ซึ่งเป็น peoperty ของ UISplitViewController จากนั้นนำ split view โยนลงไปใน window ก็เป็นอันเสร็จเรียบร้อย
จากขั้นตอนดังกล่าวเราจะได้ code หน้าตาประมาณนี้
ดังที่กล่าวมาแล้วว่าโดยทั่วไปหากมีการแสดงผลใน portrait mode แล้ว master pane จะหายไป และควรจะมีปุ่มแปะที่ toolbar/ navigation bar ของ detail pane เพื่อเอาไว้เรียกใช้ master pane ที่หายไปขึ้นมาใช้งานได้ใหม่ ดังนั้นส่วนที่น่าจับตามองก็คือ ส่วนนี้
master view จะถูกแสดงขึ้นมาใน popover view และนี่เป็นการกำหนดว่าหาก view นี้ถูกแสดงขึ้นมาด้วย Popover view แล้วจะให้มีขนาดเป็นเท่าไหร่
จังหวะในการเพิ่มปุ่มที่ detail pane เพื่อใช้สำหรับเรียก master view ขึ้นมาเวลาอยู่ใน portrait mode เราจะทำใน split view delegate method 2 method นี้
delegate method ด้านบนจะทำงานเมื่อ pane ทางซ้ามือต้องหายไปใน portrait mode
โดยเราจะได้รับ bar button item ที่พร้อมตอบรับกับการถูกกด และแสดง popover view ขึ้นมาให้ เราเพียงแค่กำหนด title ให้มันเท่านั้นว่าจะให้้มันมี title ว่าอย่างไร
จากนั้นเก็บ pc (popover controller) ที่ถูกส่งเข้ามาไว้ใช้งานต่อไปในเหตุการณ์ด้านล่างนี้
delegate method ด้านบนจะทำงานเมื่อการแสดงผลกลับไปสู่ landscape mode และ master pane กำลังกลับมา จากโค๊ดเราจะเอา left bar button item ออกจาก detail pane พร้อมทั้งสั่งกำหนด popover controller ที่เราเคยเก็บไว้ให้เป็น nil ไปซะ
เพียงเท่านี้เราก็จะได้การใช้งาน split view ร่วมกับ popover view มาใช้อย่างง่ายๆแล้ว
Related Link from Roti
Split View ช่วยให้เราแบ่งหน้าจอการทำงานออกเป็น 2 ข้าง โดย panel ทางด้านซ้ายจะมีความกว้างตายตัวคือ 320 pixels ส่วน panel ทางขวา นั้นจะกว้างตามขนาดของหน้าจอที่เหลือ โดยทั่วไปเมื่อหน้าจอเป็น landscape แล้ว split view จะแสดงทั้ง 2 pane (ต่อไปจะเรียก pane ทางซ้ายว่า Master pane และเรียก pane ทางขวาว่า Detail pane) แต่หากเรา rotate หน้าจอเป็น portrait แล้ว master pane ก็จะหายไปเหลือแต่ detail pane ที่ยังอยู่
โดยทั่วไป หากเราต้องการใช้งาน master pane ที่หายไปใน portrait mode จะใช้้วิธีเพิ่มปุ่มลงไปที่ toolbar/navigation bar ของ detail pane และเมื่อกดปุ่มนั้นแล้ว pane ทางซ้ายจะถูกแสดงขึ้นมาใน Popover view ดังรูปด้านล่าง
Popover view จะเป็นหน้าต่างเล็กๆที่จะแสดงขึ้นมาเมื่อเรากดปุ่มอะไรบางอย่าง โดยพบเห็นได้ทั่วไปในโปรแกรมที่มีการใช้ split view อยู่แล้ว
และนี่คือลักษณะโดยทั่วไปของการใช้งาน split view เป็น landscape mode จะแสดงทั้งสอง pane หากเป็น portrait จะแสดงแค่ detail pane และเมื่อมีการกดปุ่มที่ toolbar/navigation bar จะแสดง popover view ออกมา ซึ่งในนั้นบรรจุ master pane อยู่ด้านใน ดังรูปด้านล่าง
สำหรับตัวอย่างของ split pane ให้หาดูได้จากโปรแกรมอย่างเช่น Notes และ Mail ที่ติดมากับ iPad
ลองใช้ Split View กันดีกว่า
หลักการก็คือ สร้าง split view ขึ้นมาจาก UISplitViewController จากนั้นสร้าง view controller อีก 2 ตัว และนำมากำหนดค่าให้กับ viewControllers ซึ่งเป็น peoperty ของ UISplitViewController จากนั้นนำ split view โยนลงไปใน window ก็เป็นอันเสร็จเรียบร้อย
จากขั้นตอนดังกล่าวเราจะได้ code หน้าตาประมาณนี้
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch
_masterViewController = [[MainViewController alloc] init];
_masterViewController.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
_masterViewController.view.backgroundColor = [UIColor lightGrayColor];
_detailViewController = [[DetailViewController alloc] init];
_detailViewController.view.backgroundColor = [UIColor grayColor];
_navigationController = [[UINavigationController alloc] initWithRootViewController: _masterViewController];
_detailNavigationController = [[UINavigationController alloc] initWithRootViewController: _detailViewController];
_splitVC = [[UISplitViewController alloc] init];
_splitVC.viewControllers = [NSArray arrayWithObjects: _navigationController, _detailNavigationController, nil];
_splitVC.delegate = self;
[window addSubview: _splitVC.view];
[window makeKeyAndVisible];
return YES;
}
ดังที่กล่าวมาแล้วว่าโดยทั่วไปหากมีการแสดงผลใน portrait mode แล้ว master pane จะหายไป และควรจะมีปุ่มแปะที่ toolbar/ navigation bar ของ detail pane เพื่อเอาไว้เรียกใช้ master pane ที่หายไปขึ้นมาใช้งานได้ใหม่ ดังนั้นส่วนที่น่าจับตามองก็คือ ส่วนนี้
_masterViewController.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0);
master view จะถูกแสดงขึ้นมาใน popover view และนี่เป็นการกำหนดว่าหาก view นี้ถูกแสดงขึ้นมาด้วย Popover view แล้วจะให้มีขนาดเป็นเท่าไหร่
จังหวะในการเพิ่มปุ่มที่ detail pane เพื่อใช้สำหรับเรียก master view ขึ้นมาเวลาอยู่ใน portrait mode เราจะทำใน split view delegate method 2 method นี้
- (void)splitViewController: (UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController: (UIPopoverController*)pc {
barButtonItem.title = @"Master List";
[_detailNavigationController.navigationBar.topItem setLeftBarButtonItem:barButtonItem animated:YES];
_popoverController = pc;
}
โดยเราจะได้รับ bar button item ที่พร้อมตอบรับกับการถูกกด และแสดง popover view ขึ้นมาให้ เราเพียงแค่กำหนด title ให้มันเท่านั้นว่าจะให้้มันมี title ว่าอย่างไร
- (void)splitViewController: (UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem {
[_detailNavigationController.navigationBar.topItem setLeftBarButtonItem:nil animated:YES];
_popoverController = nil;
}
เพียงเท่านี้เราก็จะได้การใช้งาน split view ร่วมกับ popover view มาใช้อย่างง่ายๆแล้ว
Related Link from Roti


ขอบคุณสำหรับบทความดีๆค่ะ
ReplyDeleteมีเรื่องรบกวนถามค่ะ
พี่ค่ะ ถ้าทำปุ่ม popover view ที่ให้โชว์ตอน หมุนเครื่องเป็นแนวนอน ปุ่มจะหาย ถ้าเป็นแนวตั้ง ปุ่มก็จะโชว์ คือ ถ้าเราสร้าง ViewController ขึ้นมาอีก ViewController
โดยให้คลิกหน้า หน้า DetailViewController เพื่อเปลี่ยน ViewController พอเปลียน ตรงnavigation ก็จะมีปุ่ม back ค่ะ คือว่าจะถามว่าเราสามารถ ทำให้ปุ่ม popover view ขึ้นในหน้านี้ด้วยได้ไหมค่ะ
อธิบายแล้วงงๆ ไหมหว่าา
งงครับ :p แต่ถ้าเข้าใจไม่ผิด ทำได้ครับ เราเอา popover ไปวางไว้ตรงไหนก็ได้ครับ concept ของมันคือต้องกำหนดจุดอิงของมันด้วย แล้วมันจะโผล่ออกมาให้เราโดยอิงจากจุดที่เรากำหนดให้เองครับ
ReplyDeleteถึงตอนนี้คุณ pondly คงทำได้แล้วมั้งครับ ฮาๆ นานไปหน่อย
ขอบคุณสำหรับวิธีดีๆครับ
ReplyDeleteนอกเรื่องนิดนึงครับ ผมอยากใส่ MKMapView ในหน้า Detail อ่ะครับ ตอนทำใน iphone ก็ปกติดีครับ แต่พอมาทำใน ipad มัน error อ่ะครับ อยากทราบว่าทำไม รบกวนหน่อยได้ไม๊ครับ
ขอบคุณล่วงหน้าครับ
มัน error ว่าอะไรบ้างครับ ลองดู console แล้วบอกมาได้ไหมครับ?
ReplyDeleteก่อนอื่นขอบคุณที่ตอบไวมากๆครับ
ReplyDelete***Terminating app due to uncaught exception 'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjecrForKey:]: cannot decode ovject of class (MKMapView)'
*** Call stack at first throw
ผมใช้ xcode4 นะครับ
ซึ่งวิธีที่ผมทำก็คือ ลากMapView ลงไปในหน้า Xib โยง mapView ไปที่หน้า mapViewController.h ครับ ให้มัน @property ให้เองอัตโนมัติ จากนั้นหน้า mapView ก็ควรจะแสดงใช่ไม๊ครับ แต่พอ run แล้ว SIGABRT ครับ (ผมทำตรงไหนผิดรึเปล่าครับ)
และหากลบmapView ออกจาหน้า xib แล้ว ก็จะไม่มีปัญหาครับ
ขอบคุณครับ
link กับ MapKit Framework หรือยังครับ?
ReplyDeleteผม link mapkit.framework ผิดวิธีครับ ตอนนี้ได้แล้วครับ
ReplyDeleteปล.ผมพึ่งเริ่มเขียนมาได้ซักเดือนนึงครับ ยังไม่ค่อยเก่งขอโทษที่ทำให้เสียเวลาครับ
ปล2.เว็ปนี้ดีมากครับ จะตามดู tips ต่อๆไปนะครับ ขอบคุณมากครับ
สู้ๆ ครับ ยินดีตอบคำถาม และขอบคุณมากเช่นกันครับ :)
ReplyDeleteพี่ครับ ถ้าพอมีเวลาว่างก็ขอรบกวนด้วยครับ
ReplyDeleteคือผมทำ Map อยู่ตอนนี้ ในPopover ของผมมี 7-Eleven อยู่ครับ แล้วทีนี้ผมต้องการให้กดที่ roll นั้น แล้วหน้า DetailViewController refresh ใหม่ โดยมีหมุดมาปักในตำแหน่งที่กำหนดไว้
ปัญหาตอนนี้คือ พอกดroll ใน table แล้ว หมุดไม่ปัก และผมก็ไม่ทราบวิธีการ refresh mapview ใหม่ด้วยครับ
ยังไงก็รบกวนด้วยครับ ขอบคุณครับ
ให้ฝึกทำตามอันนี้ครับ http://www.raywenderlich.com/2847/introduction-to-mapkit-on-ios-tutorial
ReplyDeleteถ้าอ้างอิงจาก tutorial ใน link ที่ผมให้ไปจะทำได้แบบนี้ครับ
1. remove annotation ทุกๆ อันบน Map ออกไปก่อน
for (id annotation in _mapView.annotations) {
[_mapView removeAnnotation:annotation];
}
2. จากนั้นวนลูปแปะ Annotation ลงบน Map ใหม่ครับ
for (NSArray * row in data) {
NSNumber * latitude = [row objectAtIndex:11];
NSNumber * longitude = [row objectAtIndex:12];
NSString * crime = [row objectAtIndex:16];
NSString * address = [row objectAtIndex:15];
CLLocationCoordinate2D coordinate;
coordinate.latitude = latitude.doubleValue;
coordinate.longitude = longitude.doubleValue;
MyLocation *annotation = [[[MyLocation alloc] initWithName:crime address:address coordinate:coordinate] autorelease];
[_mapView addAnnotation:annotation];
}
ลองเอาไปปรับใช้ดูครับ :)
ขอบคุณมากครับ ตอนนี้ผมสามารถปักหมุดลงไปในจุดที่ต้องการได้แล้ว แต่ยังจำเป็นต้องกดปุ่ม refresh อยู่ครับ คือใน tutorial ที่พี่ให้มาเป็น iphone ใช่มั๊ยครับ ทีนี้การที่จะสั่งงานจาก master pane ให้ Detail pane มีการเปลี่ยนแปลงผมยังทำไม่ได้ครับ ที่ผมทำได้ตอนนี้คือ ในmaster pane ที่เป็น table พอกด roll ก็ให้ set latitude longitude ใหม่ จากนั้น กลับมากดปุ่มปักหมุดที่หน้า detailview ใหม่ครับ ผมอยากได้แบบกดปุ๊บ detail view มีหมุดมาปักปั๊ปเลยอ่ะครับ ไม่ทราบว่าทำได้รึเปล่า ยังไงก็รบกวนด้วยนะครับ ขอบคุณมากครับ
ReplyDeleteได้แล้วครับ ขอบคุณมากครับ
ReplyDeleteที่ได้เพราะว่า ตอนแรก ใช้ DetailViewController *dTVC = [[DetailViewController alloc] init];
แล้วแก้เป็น DetailViewController *dTVC = (DetailViewController *)[self.splitViewController.viewController lastObject];
ครับ
ขอโทษทีครับที่ไม่ได้มาตอบ
ReplyDeleteเท่าที่ดูก็คือตอนนี้คุณ Anonymous มีปัญหาเกี่ยวกับการส่งข้อมูลไปมาระหว่าง ViewController น่ะครับ เดี๋ยวพอทำไปสักพักแล้วจะเริ่มค้นพบ pattern ในการเขียนโปแกรมเอง ลองศึกษา delegate pattern ดูครับ