แอปพลิเคชันของฉันต้องการเพิ่มสิ่งต่อไปนี้ในเอกสารการดำเนินการ
- UIToolbar
- ปุ่มบน UIToolbar
- การควบคุม UIPicker
ฉันได้รวมรูปภาพเพื่อทำความเข้าใจข้อกำหนดของฉัน
คุณช่วยอธิบายได้ไหมว่าสามารถใช้งานได้อย่างไร
แอปพลิเคชันของฉันต้องการเพิ่มสิ่งต่อไปนี้ในเอกสารการดำเนินการ
ฉันได้รวมรูปภาพเพื่อทำความเข้าใจข้อกำหนดของฉัน
คุณช่วยอธิบายได้ไหมว่าสามารถใช้งานได้อย่างไร
คำตอบ:
อัปเดตสำหรับ iOS 7
เอกสารของ Apple สำหรับ UIActionSheet :UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy
ฉันไม่แนะนำให้พยายามปรับแต่งเนื้อหาของ ActionSheet เนื่องจากอาจนำไปสู่ข้อผิดพลาดของบริบทที่ไม่ถูกต้องอย่างร้ายแรงใน iOS 7 ฉันใช้เวลาเพียงไม่กี่ชั่วโมงในการแก้ไขปัญหานี้และในที่สุดก็ตัดสินใจที่จะใช้แนวทางอื่น ฉันแทนที่การเรียกเพื่อแสดงแผ่นการดำเนินการด้วยตัวควบคุมมุมมองแบบโมดอลที่มี tableview อย่างง่าย
มีหลายวิธีที่จะทำให้สำเร็จ นี่เป็นวิธีหนึ่งที่ฉันเพิ่งใช้ในโครงการปัจจุบัน เป็นเรื่องดีเพราะฉันสามารถใช้ซ้ำได้ระหว่าง 5 หรือ 6 หน้าจอที่แตกต่างกันซึ่งฉันให้ผู้ใช้ทุกคนเลือกจากรายการตัวเลือก
SimpleTableViewController
ใหม่SimpleTableViewControllerDelegate
ที่มีวิธีการที่จำเป็นและคุณสมบัติอ่อนที่เรียกว่าผู้ร่วมประชุมจากประเภทitemSelectedatRow:
id<SimpleTableViewControllerDelegate>
นี่คือวิธีที่เราจะส่งการเลือกกลับไปยังตัวควบคุมหลักitemSelectedatRow:
tableView:didSelectRowAtIndexPath:
วิธีนี้มีประโยชน์เพิ่มเติมในการใช้ซ้ำได้อย่างเป็นธรรม ในการใช้ให้อิมพอร์ตคลาส SimpleTableViewController ใน ViewController.h ของคุณสอดคล้องกับ SimpleTableViewDelegate และใช้itemSelectedAtRow:
เมธอด จากนั้นในการเปิดโมดอลเพียงสร้างอินสแตนซ์ SimpleTableViewController ใหม่ตั้งค่าข้อมูลตารางและมอบหมายและนำเสนอ
UINavigationController *navigationController = (UINavigationController *)[self.storyboard instantiateViewControllerWithIdentifier:@"SimpleTableVC"];
SimpleTableViewController *tableViewController = (SimpleTableViewController *)[[navigationController viewControllers] objectAtIndex:0];
tableViewController.tableData = self.statesArray;
tableViewController.navigationItem.title = @"States";
tableViewController.delegate = self;
[self presentViewController:navigationController animated:YES completion:nil];
ฉันจะสร้างตัวอย่างง่ายๆและโพสต์ไว้บน GitHub
ยังเห็นแสดง actionsheet ทำให้เกิดข้อผิดพลาด
อีกวิธีหนึ่ง:
ไม่มีแถบเครื่องมือ แต่มีการควบคุมแบบแบ่งส่วน (eyecandy)
UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil
delegate:nil
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
[actionSheet setActionSheetStyle:UIActionSheetStyleBlackTranslucent];
CGRect pickerFrame = CGRectMake(0, 40, 0, 0);
UIPickerView *pickerView = [[UIPickerView alloc] initWithFrame:pickerFrame];
pickerView.showsSelectionIndicator = YES;
pickerView.dataSource = self;
pickerView.delegate = self;
[actionSheet addSubview:pickerView];
[pickerView release];
UISegmentedControl *closeButton = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObject:@"Close"]];
closeButton.momentary = YES;
closeButton.frame = CGRectMake(260, 7.0f, 50.0f, 30.0f);
closeButton.segmentedControlStyle = UISegmentedControlStyleBar;
closeButton.tintColor = [UIColor blackColor];
[closeButton addTarget:self action:@selector(dismissActionSheet:) forControlEvents:UIControlEventValueChanged];
[actionSheet addSubview:closeButton];
[closeButton release];
[actionSheet showInView:[[UIApplication sharedApplication] keyWindow]];
[actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
แม้ว่าคำถามนี้จะเก่า แต่ฉันจะพูดถึงอย่างรวดเร็วว่าฉันได้รวมคลาส ActionSheetPickerพร้อมฟังก์ชั่นอำนวยความสะดวกดังนั้นคุณสามารถวาง ActionSheet ด้วย UIPickerView ในบรรทัดเดียว มันขึ้นอยู่กับรหัสจากคำตอบสำหรับคำถามนี้
แก้ไข: ตอนนี้ยังรองรับการใช้ DatePicker และ DistancePicker
ActionSheetDatePicker
โหมดนี้คุณสามารถเพิ่มปุ่มได้หลายปุ่มในแถบเครื่องมือที่ด้านบน มันเป็นไปได้ด้วยปกติActionSheetStringPicker
เกินไป?
เออ! ในที่สุดฉันก็พบมัน
ติดตั้งรหัสต่อไปนี้ในเหตุการณ์การคลิกปุ่มของคุณเพื่อเปิดแผ่นการดำเนินการตามที่ระบุในภาพคำถาม
UIActionSheet *aac = [[UIActionSheet alloc] initWithTitle:@"How many?"
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
UIDatePicker *theDatePicker = [[UIDatePicker alloc] initWithFrame:CGRectMake(0.0, 44.0, 0.0, 0.0)];
if(IsDateSelected==YES)
{
theDatePicker.datePickerMode = UIDatePickerModeDate;
theDatePicker.maximumDate=[NSDate date];
}else {
theDatePicker.datePickerMode = UIDatePickerModeTime;
}
self.dtpicker = theDatePicker;
[theDatePicker release];
[dtpicker addTarget:self action:@selector(dateChanged) forControlEvents:UIControlEventValueChanged];
pickerDateToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, 320, 44)];
pickerDateToolbar.barStyle = UIBarStyleBlackOpaque;
[pickerDateToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(DatePickerDoneClick)];
[barItems addObject:doneBtn];
[pickerDateToolbar setItems:barItems animated:YES];
[aac addSubview:pickerDateToolbar];
[aac addSubview:dtpicker];
[aac showInView:self.view];
[aac setBounds:CGRectMake(0,0,320, 464)];
คำตอบที่ยอดเยี่ยมของ Marcio สำหรับคำถามนี้เป็นประโยชน์อย่างยิ่งสำหรับฉันในการเพิ่มมุมมองย่อยใด ๆ ลงใน UIActionSheet
ด้วยเหตุผลที่ (ยัง) ไม่ชัดเจนสำหรับฉันคุณสามารถตั้งค่าขอบเขตของ UIActionSheet ได้หลังจากที่แสดงแล้วเท่านั้น ทั้งโซลูชันของ sagar และ marcio สามารถแก้ไขปัญหานี้ได้สำเร็จด้วยข้อความ setBounds: CGRectMake (... ) ที่ถูกส่งไปยังแผ่นการดำเนินการหลังจากที่แสดง
อย่างไรก็ตามการตั้งค่าขอบเขต UIActionSheet หลังจากที่แสดงแผ่นงานแล้วจะทำให้เกิดการเปลี่ยนแปลงอย่างรวดเร็วเมื่อ ActionSheet ปรากฏขึ้นโดยที่มัน "โผล่" เข้ามาในมุมมองจากนั้นจะเลื่อนเฉพาะในช่วง 40 พิกเซลสุดท้ายหรือมากกว่านั้น
เมื่อปรับขนาด UIPickerView หลังจากเพิ่มมุมมองย่อยฉันขอแนะนำให้ตัดข้อความ setBounds ที่ส่งไปยัง actionSheet ภายในบล็อกภาพเคลื่อนไหว วิธีนี้จะทำให้ทางเข้าของ actionSheet ดูราบรื่นขึ้น
UIActionSheet *actionSheet = [[[UIActionSheet alloc] initWithTitle:nil delegate:nil cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
// add one or more subviews to the UIActionSheet
// this could be a UIPickerView, or UISegmentedControl buttons, or any other
// UIView. Here, let's just assume it's already set up and is called
// (UIView *)mySubView
[actionSheet addSubview:myView];
// show the actionSheet
[actionSheet showInView:[UIApplication mainWindow]];
// Size the actionSheet with smooth animation
[UIView beginAnimations:nil context:nil];
[actionSheet setBounds:CGRectMake(0, 0, 320, 485)];
[UIView commitAnimations];
สำหรับคนที่กำลังค้นหาฟังก์ชัน DatePickerDoneClick ... นี่คือรหัสง่ายๆในการปิด Action Sheet เห็นได้ชัดว่า aac ควรเป็น ivar (อันที่อยู่ในไฟล์. h ของคุณ)
- (void)DatePickerDoneClick:(id)sender{
[aac dismissWithClickedButtonIndex:0 animated:YES];
}
ฉันไม่เข้าใจจริงๆว่าทำไมUIPickerView
มันจึงเกิดขึ้นภายในไฟล์UIActionSheet
. ดูเหมือนจะเป็นวิธีการแก้ปัญหาที่ยุ่งเหยิงและแฮ็คซึ่งอาจเสียได้ในการเปิดตัว iOS ในอนาคต (ฉันเคยมีสิ่งต่างๆเช่นการหยุดพักนี้ในแอปมาก่อนซึ่งUIPickerView
ไม่มีการนำเสนอในการแตะครั้งแรกและต้องได้รับการแก้ไขใหม่ - นิสัยแปลก ๆ กับUIActionSheet
)
สิ่งที่ฉันทำมีเพียงแค่ใช้ a UIPickerView
แล้วเพิ่มเป็นมุมมองย่อยในมุมมองของฉันและทำให้มันเคลื่อนไหวได้ราวกับว่ากำลังนำเสนอเหมือนแผ่นงาน
/// Add the PickerView as a private variable
@interface EMYourClassName ()
@property (nonatomic, strong) UIPickerView *picker;
@property (nonatomic, strong) UIButton *backgroundTapButton;
@end
///
/// This is your action which will present the picker view
///
- (IBAction)showPickerView:(id)sender {
// Uses the default UIPickerView frame.
self.picker = [[UIPickerView alloc] initWithFrame:CGRectZero];
// Place the Pickerview off the bottom of the screen, in the middle set the datasource delegate and indicator
_picker.center = CGPointMake([[UIScreen mainScreen] bounds].size.width / 2.0, [[UIScreen mainScreen] bounds].size.height + _picker.frame.size.height);
_picker.dataSource = self;
_picker.delegate = self;
_picker.showsSelectionIndicator = YES;
// Create the toolbar and place it at -44, so it rests "above" the pickerview.
// Borrowed from @Spark, thanks!
UIToolbar *pickerDateToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, -44, 320, 44)];
pickerDateToolbar.barStyle = UIBarStyleBlackTranslucent;
[pickerDateToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
// The action can whatever you want, but it should dimiss the picker.
UIBarButtonItem *doneBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(backgroundTapped:)];
[barItems addObject:doneBtn];
[pickerDateToolbar setItems:barItems animated:YES];
[_picker addSubview:pickerDateToolbar];
// If you have a UITabBarController, you should add the picker as a subview of it
// so it appears to go over the tabbar, not under it. Otherwise you can add it to
// self.view
[self.tabBarController.view addSubview:_picker];
// Animate it moving up
[UIView animateWithDuration:.3 animations:^{
[_picker setCenter:CGPointMake(160, [[UIScreen mainScreen] bounds].size.height - 148)]; //148 seems to put it in place just right.
} completion:^(BOOL finished) {
// When done, place an invisible button on the view behind the picker, so if the
// user "taps to dismiss" the picker, it will go away. Good user experience!
self.backgroundTapButton = [UIButton buttonWithType:UIButtonTypeCustom];
_backgroundTapButton.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
[_backgroundTapButton addTarget:self action:@selector(backgroundTapped:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_backgroundTapButton];
}];
}
// And lastly, the method to hide the picker. You should handle the picker changing
// in a method with UIControlEventValueChanged on the pickerview.
- (void)backgroundTapped:(id)sender {
[UIView animateWithDuration:.3 animations:^{
_picker.center = CGPointMake(160, [[UIScreen mainScreen] bounds].size.height + _picker.frame.size.height);
} completion:^(BOOL finished) {
[_picker removeFromSuperview];
self.picker = nil;
[self.backgroundTapButton removeFromSuperview];
self.backgroundTapButton = nil;
}];
}
หากต้องการเพิ่มโซลูชันที่ยอดเยี่ยมของ marcio dismissActionSheet:
สามารถดำเนินการได้ดังนี้
เพิ่มวิธีนี้ในรหัสของคุณ
- (void)dismissActionSheet:(id)sender{
[_actionSheet dismissWithClickedButtonIndex:0 animated:YES];
[_myButton setTitle:@"new title"]; //set to selected text if wanted
}
ฉันคิดว่านี่เป็นวิธีที่ดีที่สุดที่จะทำ
เป็นสิ่งที่ทุกคนแนะนำ แต่ใช้บล็อกซึ่งเป็นสัมผัสที่ดี!
ตั้งแต่ iOS 8 คุณไม่สามารถใช้งานไม่ได้เนื่องจาก Apple เปลี่ยนการใช้งานภายในของUIActionSheet
ไฟล์. โปรดดูเอกสารของ Apple :
Subclassing Notes
UIActionSheet ไม่ได้ออกแบบมาให้ subclassed, หรือ คุณควรเพิ่มมุมมองลำดับชั้นของ หากคุณต้องการนำเสนอชีตที่มีการปรับแต่งมากกว่าที่ UIActionSheet API จัดเตรียมไว้คุณสามารถสร้างของคุณเองและนำเสนอแบบแยกส่วนด้วย presentViewController: animated: complete :.
ฉันชอบวิธีการที่ Wayfarer และ flexaddicted แต่พบว่า (เช่น aZtral) ไม่ทำงานเนื่องจาก backgroundTapButton เป็นองค์ประกอบเดียวที่ตอบสนองต่อการโต้ตอบของผู้ใช้ สิ่งนี้ทำให้ฉันใส่ทั้งสามมุมมองย่อยของเขา: _picker, _pickerToolbar และ backgroundTapButton ไว้ในมุมมองที่มี (ป๊อปอัป) ซึ่งเป็นภาพเคลื่อนไหวในและนอกหน้าจอ ฉันต้องการปุ่มยกเลิกบน _pickerToolbar ด้วย นี่คือองค์ประกอบรหัสที่เกี่ยวข้องสำหรับมุมมองป๊อปอัป (คุณต้องจัดหาแหล่งข้อมูลตัวเลือกของคุณเองและวิธีการมอบหมาย)
#define DURATION 0.4
#define PICKERHEIGHT 162.0
#define TOOLBARHEIGHT 44.0
@interface ViewController ()
@property (nonatomic, strong) UIView *popup;
@property (nonatomic, strong) UIPickerView *picker;
@property (nonatomic, strong) UIToolbar *pickerToolbar;
@property (nonatomic, strong) UIButton *backgroundTapButton;
@end
-(void)viewDidLoad {
// These are ivars for convenience
rect = self.view.bounds;
topNavHeight = self.navigationController.navigationBar.frame.size.height;
bottomNavHeight = self.navigationController.toolbar.frame.size.height;
navHeights = topNavHeight + bottomNavHeight;
}
-(void)showPickerView:(id)sender {
[self createPicker];
[self createToolbar];
// create view container
_popup = [[UIView alloc] initWithFrame:CGRectMake(0.0, topNavHeight, rect.size.width, rect.size.height - navHeights)];
// Initially put the centre off the bottom of the screen
_popup.center = CGPointMake(rect.size.width / 2.0, rect.size.height + _popup.frame.size.height / 2.0);
[_popup addSubview:_picker];
[_popup insertSubview:_pickerToolbar aboveSubview:_picker];
// Animate it moving up
// This seems to work though I am not sure why I need to take off the topNavHeight
CGFloat vertCentre = (_popup.frame.size.height - topNavHeight) / 2.0;
[UIView animateWithDuration:DURATION animations:^{
// move it to a new point in the middle of the screen
[_popup setCenter:CGPointMake(rect.size.width / 2.0, vertCentre)];
} completion:^(BOOL finished) {
// When done, place an invisible 'button' on the view behind the picker,
// so if the user "taps to dismiss" the picker, it will go away
self.backgroundTapButton = [UIButton buttonWithType:UIButtonTypeCustom];
_backgroundTapButton.frame = CGRectMake(0, 0, _popup.frame.size.width, _popup.frame.size.height);
[_backgroundTapButton addTarget:self action:@selector(doneAction:) forControlEvents:UIControlEventTouchUpInside];
[_popup insertSubview:_backgroundTapButton belowSubview:_picker];
[self.view addSubview:_popup];
}];
}
-(void)createPicker {
// To use the default UIPickerView frame of 216px set frame to CGRectZero, but we want the 162px height one
CGFloat pickerStartY = rect.size.height - navHeights - PICKERHEIGHT;
self.picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0.0, pickerStartY, rect.size.width, PICKERHEIGHT)];
_picker.dataSource = self;
_picker.delegate = self;
_picker.showsSelectionIndicator = YES;
// Otherwise you can see the view underneath the picker
_picker.backgroundColor = [UIColor whiteColor];
_picker.alpha = 1.0f;
}
-(void)createToolbar {
CGFloat toolbarStartY = rect.size.height - navHeights - PICKERHEIGHT - TOOLBARHEIGHT;
_pickerToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0, toolbarStartY, rect.size.width, TOOLBARHEIGHT)];
[_pickerToolbar sizeToFit];
NSMutableArray *barItems = [[NSMutableArray alloc] init];
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(cancelAction:)];
[barItems addObject:cancelButton];
// Flexible space to make the done button go on the right
UIBarButtonItem *flexSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:self action:nil];
[barItems addObject:flexSpace];
// The done button
UIBarButtonItem *doneButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone target:self action:@selector(doneAction:)];
[barItems addObject:doneButton];
[_pickerToolbar setItems:barItems animated:YES];
}
// The method to process the picker, if we have hit done button
- (void)doneAction:(id)sender {
[UIView animateWithDuration:DURATION animations:^{
_popup.center = CGPointMake(rect.size.width / 2.0, rect.size.height + _popup.frame.size.height / 2.0);
} completion:^(BOOL finished) { [self destroyPopup]; }];
// Do something to process the returned value from your picker
}
// The method to process the picker, if we have hit cancel button
- (void)cancelAction:(id)sender {
[UIView animateWithDuration:DURATION animations:^{
_popup.center = CGPointMake(rect.size.width / 2.0, rect.size.height + _popup.frame.size.height / 2.0);
} completion:^(BOOL finished) { [self destroyPopup]; }];
}
-(void)destroyPopup {
[_picker removeFromSuperview];
self.picker = nil;
[_pickerToolbar removeFromSuperview];
self.pickerToolbar = nil;
[self.backgroundTapButton removeFromSuperview];
self.backgroundTapButton = nil;
[_popup removeFromSuperview];
self.popup = nil;
}