Frage

Meine Anwendung erfordert folgende Dinge in einem Aktionsblatt hinzugefügt werden.

  • UIToolbar
  • Button auf UIToolbar
  • UIPicker Kontrolle

Ich habe ein Bild enthält meine Anforderungen zu verstehen.

alt text

Könnten Sie bitte erklären, wie dies umgesetzt werden?

War es hilfreich?

Lösung

Update für iOS 7

Apple-Dokumentation für UIActionSheet : UIActionSheet is not designed to be subclassed, nor should you add views to its hierarchy

Ich empfehle vor dem Versuch, den Inhalt eines ActionSheet anpassen, da es zu schweren ungültigen Kontext Fehler in iOS führen kann 7. Ich habe nur ein paar Stunden durch dieses Problem zu arbeiten und schließlich beschlossen, einen anderen Ansatz zu nehmen. Ich ersetzte den Aufruf des Aktionsblatt mit einem modalen View-Controller zu zeigen, eine einfache Tableview enthält.

Es gibt viele Möglichkeiten, dies zu erreichen. Hier ist eine Möglichkeit, die ich gerade in einem aktuellen Projekt umgesetzt. Es ist schön, weil ich es zwischen 5 oder 6 verschiedenen Bildschirmen wiederverwenden kann, wo ich alle Benutzer aus einer Liste von Optionen wählen.

  1. Erstellen Sie eine neue UITableViewController Unterklasse, SimpleTableViewController.
  2. Erstellen Sie einen UITableViewController in Ihrem Storyboard (in einer Navigationssteuerung eingebettet) und seine individuelle Klasse SimpleTableViewController.
  3. Geben Sie den Navigationscontroller für SimpleTableViewController eine Storyboard-ID „SimpleTableVC“.
  4. In SimpleTableViewController.h, erstellen Sie eine NSArray Eigenschaft, die die Daten in der Tabelle darstellen.
  5. Auch in SimpleTableViewController.h, erstellen Sie ein Protokoll SimpleTableViewControllerDelegate mit einem Verfahren itemSelectedatRow: erforderlich, und eine schwache Eigenschaft namens Delegate vom Typ id<SimpleTableViewControllerDelegate>. Dies ist, wie wir die Auswahl zurück in der übergeordneten Steuerung übergeben werden.
  6. In SimpleTableViewController.m, implementieren die Tableview Datenquelle und Delegatmethoden, itemSelectedatRow: in tableView:didSelectRowAtIndexPath: aufrufen.

Dieser Ansatz hat den zusätzlichen Vorteil, ziemlich wiederverwendbar sein. So verwenden, importieren Sie entsprechen den SimpleTableViewController Klasse in Ihrer ViewController.h, auf die SimpleTableViewDelegate und die itemSelectedAtRow: Methode implementieren. Dann die modale zu öffnen gerade einen neuen SimpleTableViewController instanziiert, stellen Sie die Tabellendaten und delegieren, und präsentieren sie.

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

Ich erstelle ein einfaches Beispiel und es auf Github geschrieben.

Siehe auch Zeige actionsheet verursacht CGContext ungültigen Kontext Fehler .

Andere Tipps

Eine weitere Lösung:

  • keine Symbolleiste, sondern eine segmentierte Kontrolle (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)];
    

Auch wenn diese Frage ist alt, ich werde schnell erwähnen, dass ich geworfen habe zusammen ein ActionSheetPicker Klasse mit einer Komfortfunktion, so dass Sie einen ActionSheet mit einem UIPickerView in einer Zeile laichen können. Es basiert auf Code von Antworten auf diese Frage.

Edit: Es unterstützt nun auch die Verwendung eines Datepicker und DistancePicker

.

UPD:

Diese Version ist veraltet: Verwenden Sie ActionSheetPicker-3.0 statt

.

Animation

Ja! Ich kann es schließlich finden.

implementieren auf Schaltfläche Click-Ereignisse folgenden Code, Aktion Blatt Pop-up wie im Bild von Frage gegeben.

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

Die Marcio ausgezeichnete Lösung dieser Frage war eine große Hilfe für mich Subviews jeglicher Art zu einem UIActionSheet in hinzuzufügen.

Aus Gründen, die (noch) nicht gibt mir ganz klar, die Grenzen des UIActionSheet können nur dann eingestellt werden, nachdem er angezeigt wurde; beide Sagar ist und marcio Lösungen erfolgreich adressieren diese mit setBounds. CGRectMake (...) Nachricht an den actionsheet gesendet werden nach gezeigt wird

jedoch die UIActionSheet Grenzen nach der Blatteinstellung angezeigt wurde erzeugt einen sprunghaften Übergang, wenn die ActionSheet appeaars, wo es „Pop“ in Sicht, und auch dann nur in scrollt über die letzten 40 Pixel oder so.

Wenn Sie eine UIPickerView Dimensionierung nach Subviews Zugabe, empfehle ich das Einwickeln der setBounds Nachricht an den actionSheet innerhalb eines Animationsblock gesendet. Dies wird der Eingang des actionSheet glatter erscheinen machen.

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

Für die Jungs, die sind binden die DatePickerDoneClick Funktion zu finden ... hier ist der einfache Code, um das Aktionsblatt zu schließen. Offensichtlich sollte aac einen Ivar (denjenigen, der in Ihrer implmentation .h-Datei geht) sein


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

Ich verstehe wirklich nicht, warum die UIPickerView in einem UIActionSheet werden. Dies scheint eine chaotische und Hacky Lösung zu sein, die in einer zukünftigen iOS Release gebrochen werden können. (Ich habe schon in einer App wie diese Pause Dinge hat, wo die UIPickerView wurde nicht auf dem ersten Abgriff präsentiert und retapped werden mußte - seltsame Macken mit dem UIActionSheet).

Was habe ich einfach eine UIPickerView implementieren und es dann als Subview meiner Ansicht hinzugefügt, und animieren sie bewegt sich, als ob es wie ein Action-Blatt präsentiert wurden.

/// 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;
    }];
}

zu marcio ehrfürchtigen Lösung hinzuzufügen, kann dismissActionSheet: wie folgt umgesetzt werden.

  1. Fügen Sie ein ActionSheet Objekt in die H-Datei, synthetisiert und es in .m-Datei verweisen.
  2. Fügen Sie diese Methode, um Ihren Code.

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

Ich denke, der beste Weg ist, es zu tun.

ActionSheetPicker-3.0

Es ist ziemlich viel, was jeder vorschlagen, sondern nutzt Blöcke, die eine nette Geste!

Seit iOS 8, können Sie nicht, es funktioniert nicht, weil Apple-interne Implementierung von UIActionSheet geändert. Bitte beachten href="https://developer.apple.com/library/ios/documentation/uikit/reference/uiactionsheet_class/Reference/Reference.html" rel="nofollow"> Apple-Dokumentation

  

Subclassing Hinweise

     

UIActionSheet ist nicht als Unterklasse entworfen, noch    sollten Sie den Blick auf seine Hierarchie hinzufügen. Wenn Sie ein Blatt mit mehr Anpassungs präsentieren müssen als durch die UIActionSheet API zur Verfügung gestellt, können Sie Ihre eigenen erstellen und präsentieren sie modal mit presentViewController: animated: Abschluss:.

mochte ich den Ansatz von Wayfarer genommen und flexaddicted, fand aber (wie aZtral), dass es nicht funktioniert hat, wie die backgroundTapButton das einzige Element war, der Benutzer-Interaktion wurde reagiert. Dies führte mich alle drei seiner Subviews zu setzen: _picker, _pickerToolbar und backgroundTapButton innerhalb einer enthaltenden Ansicht (Popup), die dann auf und neben dem Bildschirm animiert wurde. Ich brauchte auch eine Abbrechen-Taste auf dem _pickerToolbar. Hier sind die entsprechenden Codeelemente für die Popup-Ansicht (müssen Sie Ihre eigene Picker Datenquelle und Delegatmethoden liefern).

#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;
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top