在操作中添加UIPickerView&abutton – 如何?
我的应用程序需要在操作表中添加以下内容。
- UIToolbar
- UIToolbar上的button
- UIPicker控制
我已经包含一张图片来理解我的要求。
你能解释一下,这可以如何实施?
iOS 7更新
UIActionSheet的Apple文档 : UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy
我build议不要尝试自定义ActionSheet的内容,因为它可能导致iOS 7中严重的无效上下文错误。我只花了几个小时来解决这个问题,最终决定采取不同的方法。 我用一个包含简单tableview的模式视图控制器来replace调用来显示动作表。
有很多方法可以做到这一点。 这是我刚刚在当前项目中实现的一种方法。 这很好,因为我可以在5到6个不同的屏幕之间重复使用它,我可以从所有的用户中select一个。
- 创build一个新的UITableViewController子类
SimpleTableViewController
。 - 在故事板中创build一个UITableViewController(embedded在导航控制器中),并将其自定义类设置为SimpleTableViewController。
- 为SimpleTableViewController的导航控制器提供“SimpleTableVC”的Storyboard ID。
- 在SimpleTableViewController.h中,创build一个表示表中数据的NSArray属性。
- 同样在SimpleTableViewController.h中,使用必需的方法
itemSelectedatRow:
创build一个协议SimpleTableViewControllerDelegate
,以及一个称为委托types为id<SimpleTableViewControllerDelegate>
的弱属性。 这是我们将如何将select传递回父控制器。 - 在SimpleTableViewController.m中,实现tableview数据源和委托方法,在
tableView:didSelectRowAtIndexPath:
调用itemSelectedatRow:
这种方法具有相当可重用的附加好处。 要使用,在您的ViewController.h中导入SimpleTableViewController类,符合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];
我创build一个简单的例子,并发布在github上 。
另请参阅显示动作表导致CGContext无效的上下文错误 。
另一个解决scheme:
-
没有工具栏,但分段控制(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类抛在一起,所以你可以在一行中产生一个带有UIPickerView的ActionSheet。 它基于这个问题的答案代码。
编辑:它现在还支持使用DatePicker和DistancePicker。
UPD:
此版本已弃用:改为使用ActionSheetPicker-3.0 。
是的! 我终于find了。
在你的button点击事件上实现下面的代码,popup问题图片中给出的操作表。
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对这个问题的出色解决scheme对于我在UIActionSheet中添加任何types的子视图都非常有帮助。
由于还没有完全清楚的原因,UIActionSheet的界限只能在显示后才能设置。 sagar和marcio的解决scheme都成功地解决了这个问题,在显示之后,setBounds:CGRectMake(…)消息被发送到动作表。
但是,在表单显示之后设置UIActionSheet边界会在ActionSheet出现时popup一个跳跃的转换,它会“popup”到视图中,然后只滚动到最后的40个像素左右。
在添加子视图之后调整UIPickerView的大小时,我build议将发送给actionSheet的setBounds消息包装在animation块中。 这将使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];
对于那些试图findDatePickerDoneClick函数的人…这里是简单的代码来解除操作表。 显然aac应该是一个ivar(在你的implmentation.h文件中的那个)
- (void)DatePickerDoneClick:(id)sender{ [aac dismissWithClickedButtonIndex:0 animated:YES]; }
我不明白为什么UIPickerView
进入UIActionSheet
。 这似乎是一个混乱和hacky的解决scheme,可以在未来的iOS版本中打破。 (之前我曾经有过这样的应用程序中断事件, UIPickerView
并没有在第一次点击时出现,而且必须被重新存储 – 与UIActionSheet
奇怪怪癖)。
我所做的只是实现一个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的真棒解决scheme, dismissActionSheet:
可以执行如下。
- 将一个ActionSheet对象添加到.h文件中,将其合成并在.m文件中引用它。
-
将此方法添加到您的代码中。
- (void)dismissActionSheet:(id)sender{ [_actionSheet dismissWithClickedButtonIndex:0 animated:YES]; [_myButton setTitle:@"new title"]; //set to selected text if wanted }
我认为这是最好的办法。
ActionSheetPicker-3.0
它几乎所有人都build议,但使用块,这是一个很好的接触!
由于iOS 8,你不能,它不工作,因为苹果改变了UIActionSheet
内部实现。 请参阅Apple文档 :
子类化笔记
UIActionSheet不是被devise为子类, 也不 应该添加视图到它的层次结构中 。 如果您需要提供比UIActionSheet API提供的更多定制的工作表,您可以创build自己的模型并使用presentViewController呈现它:animated:completion :.
我喜欢Wayfarer采取的方法,但是发现(比如aTral),它并不工作,因为backgroundTapButton是响应用户交互的唯一元素。 这导致我把他的所有三个子视图:_picker,_pickerToolbar和backgroundTapButton包含在一个包含视图(popup),然后在屏幕上和屏幕上animation。 我还需要_pickerToolbar上的取消button。 下面是popup视图的相关代码元素(您需要提供自己的select器数据源和委托方法)。
#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; }