Добавить UIPickerView и кнопку в листе действий - как?

Мое приложение требует следующих вещей, которые нужно добавить в лист действий.

  • UIToolbar
  • Кнопка на UIToolbar
  • Контроль UIPicker

Я включил изображение, чтобы понять мои требования.

alt text

Не могли бы вы объяснить, как это можно реализовать?

Ответ 1

Обновление для iOS 7

Документы Apple для UIActionSheet: UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy

Я рекомендую не пытаться настроить содержимое ActionSheet, так как это может привести к серьезным недопустимым контекстным ошибкам в iOS 7. Я просто потратил несколько часов на работу над этой проблемой и в конечном итоге решил использовать другой подход. Я заменил вызов, чтобы показать панель действий с модульным контроллером вида, содержащим простой просмотр таблиц.

Есть много способов добиться этого. Здесь один из способов, который я только что реализовал в текущем проекте. Это хорошо, потому что я могу повторно использовать его между 5 или 6 различными экранами, где я все пользователи выбираю из списка параметров.

  • Создайте новый подкласс UITableViewController, SimpleTableViewController.
  • Создайте UITableViewController в своем раскадровке (встроенном в контроллер навигации) и установите свой собственный класс в SimpleTableViewController.
  • Дайте контроллеру навигации для SimpleTableViewController идентификатор раскадровки "SimpleTableVC".
  • В SimpleTableViewController.h создайте свойство NSArray, которое будет представлять данные в таблице.
  • Также в SimpleTableViewController.h создайте протокол SimpleTableViewControllerDelegate с требуемым методом itemSelectedatRow: и слабым свойством, называемым делегатом типа id<SimpleTableViewControllerDelegate>. Вот как мы передадим выбор обратно родительскому контроллеру.
  • В SimpleTableViewController.m реализуем методы источника данных tableview и делегата, вызывая itemSelectedatRow: в tableView:didSelectRowAtIndexPath:.

Этот подход имеет дополнительное преимущество в том, что он достаточно многоразовый. Чтобы использовать, импортируйте класс SimpleTableViewController в ViewController.h, совместите с SimpleTableViewDelegate и реализуйте метод itemSelectedatRow:. Затем, чтобы открыть modal, просто создайте новый 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.

Также см. Отображение таблиц действий вызывает CGContext недопустимые контекстные ошибки.

Ответ 2

Еще одно решение:

  • нет панели инструментов, но сегментированный элемент управления (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)];
    

Ответ 3

Несмотря на то, что этот вопрос старый, я быстро упомяну, что я собрал класс ActionSheetPicker с удобной функцией, так что вы можете создать ActionSheet с UIPickerView в одной строке. Это основано на коде из ответов на этот вопрос.

Изменение: теперь также поддерживает использование DatePicker и DistancePicker.


UPD:

Эта версия устарела: используйте взамен ActionSheetPicker-3.0.

animation

Ответ 4

Да! Я наконец нашел его.

реализовать следующий код в событии нажатия кнопки, чтобы открыть панель действий, указанную в изображении вопроса.

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)];

Ответ 5

Marcio превосходное решение этого вопроса оказало большую помощь мне в добавлении подсмотров любого вида в UIActionSheet.

По причинам, которые пока не полностью понятны мне, границы UIActionSheet могут быть установлены только после того, как они были отображены; оба решения sagar и marcio успешно справляются с этим, когда сообщение setBounds: CGRectMake (...) отправляется на лист действий после.

Однако установка границ UIActionSheet после отображения листа создает переходный переход, когда ActionSheet подтягивается, где он "появляется", а затем прокручивается только за последние 40 пикселей или около того.

При определении размера UIPickerView после добавления subviews я рекомендую обернуть сообщение 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 just assume it 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]; 

Ответ 6

Для тех парней, которые связывают, чтобы найти функцию DatePickerDoneClick... вот простой код, чтобы отклонить лист действий. Очевидно, aac должен быть ivar (тот, который входит в ваш файл .h).


- (void)DatePickerDoneClick:(id)sender{
    [aac dismissWithClickedButtonIndex:0 animated:YES];
}

Ответ 7

Я не понимаю, почему UIPickerView находится внутри UIActionSheet. Это кажется грязным и взломанным решением, которое может быть нарушено в будущей версии 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;
    }];
}

Ответ 8

Чтобы добавить к замечательному решению marcio, dismissActionSheet: можно реализовать следующим образом.

  • Добавить объект ActionSheet в ваш .h файл, синтезировать его и ссылаться на него в вашем .m файле.
  • Добавьте этот метод в свой код.

    - (void)dismissActionSheet:(id)sender{
      [_actionSheet dismissWithClickedButtonIndex:0 animated:YES];
      [_myButton setTitle:@"new title"]; //set to selected text if wanted
    }
    

Ответ 9

Я думаю, что это лучший способ сделать это.

ActionSheetPicker-3.0

В основном это то, что все предлагают, но использует блоки, что приятно прикоснуться!

Ответ 10

Так как iOS 8, вы не можете, это не работает, потому что Apple изменила внутреннюю реализацию UIActionSheet. См. Документация для Apple:

Замечания о подклассе

UIActionSheet не предназначен для подкласса, и следует добавить представления к своей иерархии. Если вам нужно представить лист с большей настройкой, чем предоставленный API UIActionSheet, вы можете создать свой собственный и представить его модально с помощью currentViewController: анимированный: завершение:.

Ответ 11

Мне понравился подход, сделанный 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;
}