Ich habe ein Popover aus einem UIBarButtonItem
Xcode-Storyboard (es gibt also keinen Code) wie folgt erstellt :
Das Präsentieren des Popovers funktioniert einwandfrei. Ich kann das Popover jedoch nicht verschwinden lassen, wenn ich auf das tippe, das es angezeigt hat UIBarButtonItem
.
Wenn die Taste (zum ersten Mal) gedrückt wird, wird das Popover angezeigt. Wenn die Taste erneut gedrückt wird (zum zweiten Mal), wird das gleiche Popover darüber angezeigt. Jetzt habe ich zwei Popover (oder mehr, wenn ich die Taste weiter drücke). Gemäß den Richtlinien für die Benutzeroberfläche von iOS muss das Popover beim ersten Tippen angezeigt und beim zweiten nicht mehr angezeigt werden:
Stellen Sie sicher, dass jeweils nur ein Popover auf dem Bildschirm angezeigt wird. Sie sollten nicht mehr als ein Popover (oder eine benutzerdefinierte Ansicht, die so gestaltet ist, dass sie wie ein Popover aussieht und sich so verhält) gleichzeitig anzeigen. Insbesondere sollten Sie vermeiden, gleichzeitig eine Kaskade oder Hierarchie von Popovers anzuzeigen, in der ein Popover aus einem anderen hervorgeht.
Wie kann ich das Popover schließen, wenn der Benutzer UIBarButtonItem
ein zweites Mal auf das Popover tippt ?
quelle
Antworten:
BEARBEITEN: Diese Probleme scheinen ab iOS 7.1 / Xcode 5.1.1 behoben zu sein. (Möglicherweise früher, da ich nicht alle Versionen testen konnte. Definitiv nach iOS 7.0, da ich diese getestet habe.) Wenn Sie einen Popover-Übergang von a erstellen
UIBarButtonItem
, stellt der Übergang sicher, dass durch erneutes Tippen auf den Popover der Popover eher ausgeblendet wird als ein Duplikat zu zeigen. Es funktioniert auch für die neuenUIPresentationController
Popover-Segmente, die Xcode 6 auch für iOS 8 erstellt.Da meine Lösung für diejenigen, die noch frühere iOS-Versionen unterstützen, von historischem Interesse sein kann, habe ich sie unten belassen.
Wenn Sie einen Verweis auf den Popover-Controller des Segues speichern und ihn schließen, bevor Sie ihn bei wiederholten Aufrufen von auf einen neuen Wert setzen
prepareForSegue:sender:
, vermeiden Sie nur das Problem, dass bei wiederholtem Drücken der Taste mehrere Stapel-Popover angezeigt werden - Sie können ihn immer noch nicht verwenden die Schaltfläche zum Schließen des Popovers, wie von der HIG empfohlen (und wie in Apples Apps usw. zu sehen)Sie können jedoch ARC nutzen, um schwache Referenzen für eine einfache Lösung auf Null zu setzen:
1: Segue von der Schaltfläche
Ab iOS 5 konnten Sie diese Funktion nicht mit einem Übergang von a ausführen
UIBarButtonItem
, dies ist jedoch unter iOS 6 und höher möglich. (Unter iOS 5 müssen Sie vom Ansichts-Controller selbstperformSegueWithIdentifier:
trennen und dann den Aktionsaufruf der Schaltfläche ausführen, nachdem Sie nach dem Popover gesucht haben.)2: Verwenden Sie einen Verweis auf das Popover in
-shouldPerformSegue...
@interface ViewController @property (weak) UIPopoverController *myPopover; @end @implementation ViewController - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // if you have multiple segues, check segue.identifier self.myPopover = [(UIStoryboardPopoverSegue *)segue popoverController]; } - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { if (self.myPopover) { [self.myPopover dismissPopoverAnimated:YES]; return NO; } else { return YES; } } @end
3: Es gibt keinen dritten Schritt!
Das Schöne an der Verwendung einer schwachen Nullreferenz ist, dass nach dem Entlassen des Popover-Controllers - ob programmgesteuert
shouldPerformSegueWithIdentifier:
oder automatisch durch den Benutzer, der auf eine andere Stelle außerhalb des Popovers tippt - der Ivarnil
wieder wechselt, sodass wir wieder zu unserem zurückkehren Ausgangszustand.Ohne schwache Referenzen auf Null zu setzen, müssten wir auch:
myPopover = nil
wenn es entlassen wirdshouldPerformSegueWithIdentifier:
, undpopoverControllerDidDismissPopover:
und auch dort zu setzenmyPopover = nil
(also fangen wir, wenn das Popover automatisch geschlossen wird).quelle
__weak
Ivar und eineweak
Immobilie sind hier gleichwertig. Ob Sie eine Immobilie oder einen Ivar für etwas in Ihrer Klasse verwenden, bleibt Gegenstand vieler Debatten. Ich neige dazu, bei einem Ivar zu bleiben, wenn ich vermute, dass benutzerdefinierte Accessoren und KVO niemals benötigt werden, aber die Strategie "Eigenschaften für alles" hat auch ihre Vorzüge.Ich habe die Lösung hier gefunden: https://stackoverflow.com/a/7938513/665396 In first prepareForSegue: sender: Speichern Sie in einem ivar / property den Zeiger auf den UIPopoverController und verwenden Sie diesen Zeiger, um das Popover in den nachfolgenden Aufrufen zu schließen.
... @property (nonatomic, weak) UIPopoverController* storePopover; ... - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"My segue"]) { // setup segue here [self.storePopover dismissPopoverAnimated:YES]; self.storePopover = ((UIStoryboardPopoverSegue*)segue).popoverController; ... }
quelle
popoverController
im gespeichert,destinationViewController
damit ich später problemlos darauf zugreifen kann, wenn mein benutzerdefinierter Delegat zurückruft.Ich habe dafür einen benutzerdefinierten Übergang verwendet.
1
Erstellen Sie einen benutzerdefinierten Abschnitt zur Verwendung in Storyboard:
@implementation CustomPopoverSegue -(void)perform { // "onwer" of popover - it needs to use "strong" reference to retain UIPopoverReference ToolbarSearchViewController *source = self.sourceViewController; UIViewController *destination = self.destinationViewController; // create UIPopoverController UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:destination]; // source is delegate and owner of popover popoverController.delegate = source; popoverController.passthroughViews = [NSArray arrayWithObject:source.searchBar]; source.recentSearchesPopoverController = popoverController; // present popover [popoverController presentPopoverFromRect:source.searchBar.bounds inView:source.searchBar permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; } @end
2
In der Ansichtssteuerung, die Quelle / Eingabe des Segues ist, z. B. Segue mit Aktion starten:
-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar { if(nil == self.recentSearchesPopoverController) { NSString *identifier = NSStringFromClass([CustomPopoverSegue class]); [self performSegueWithIdentifier:identifier sender:self]; } }
3
Referenzen werden von segue zugewiesen, wodurch UIPopoverController erstellt wird - wenn Popover geschlossen wird
-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar { if(self.recentSearchesPopoverController) { [self.recentSearchesPopoverController dismissPopoverAnimated:YES]; self.recentSearchesPopoverController = nil; } }
Grüße, Peter
quelle
Ich habe es gelöst und einen Brauch erstellt
ixPopoverBarButtonItem
, der entweder den Übergang auslöst oder das angezeigte Popover schließt.Was ich tue: Ich schalte die Aktion und das Ziel der Schaltfläche um, sodass entweder der Übergang ausgelöst wird oder das aktuell angezeigte Popover gelöscht wird.
Ich habe viel gegoogelt, um diese Lösung zu finden. Ich möchte nicht die Credits für die Idee nehmen, die Aktion umzuschalten. Das Einfügen des Codes in eine benutzerdefinierte Schaltfläche war mein Ansatz, um den Boilerplate-Code aus meiner Sicht auf ein Minimum zu beschränken.
Im Storyboard definiere ich die Klasse des BarButtonItem für meine benutzerdefinierte Klasse:
Dann übergebe ich das vom Segue erstellte Popover an meine benutzerdefinierte Schaltflächenimplementierung in der
prepareForSegue:sender:
Methode:- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"myPopoverSegue"]) { UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue; [(ixPopoverBarButtonItem *)sender showingPopover:popSegue.popoverController]; } }
Übrigens ... da ich mehr als eine Schaltfläche habe, die Popover auslöst, muss ich immer noch eine Referenz des aktuell angezeigten Popovers behalten und es schließen, wenn ich das neue sichtbar mache, aber das war nicht Ihre Frage ...
So habe ich mein benutzerdefiniertes UIBarButtonItem implementiert:
...Schnittstelle:
@interface ixPopoverBarButtonItem : UIBarButtonItem - (void) showingPopover: (UIPopoverController *)popoverController; @end
... und impl:
#import "ixPopoverBarButtonItem.h" @interface ixPopoverBarButtonItem () @property (strong, nonatomic) UIPopoverController *popoverController; @property (nonatomic) SEL tempAction; @property (nonatomic,assign) id tempTarget; - (void) dismissPopover; @end @implementation ixPopoverBarButtonItem @synthesize popoverController = _popoverController; @synthesize tempAction = _tempAction; @synthesize tempTarget = _tempTarget; -(void)showingPopover:(UIPopoverController *)popoverController { self.popoverController = popoverController; self.tempAction = self.action; self.tempTarget = self.target; self.action = @selector(dismissPopover); self.target = self; } -(void)dismissPopover { [self.popoverController dismissPopoverAnimated:YES]; self.action = self.tempAction; self.target = self.tempTarget; self.popoverController = nil; self.tempAction = nil; self.tempTarget = nil; } @end
ps: Ich bin neu bei ARC, daher bin ich mir nicht ganz sicher, ob ich hier undicht bin. Bitte sag mir, ob ich ...
quelle
Ich habe dieses Problem gelöst, ohne eine Kopie von a aufbewahren zu müssen
UIPopoverController
. Behandeln Sie einfach alles im Storyboard (Symbolleiste, BarButtons usw.) undHier ist der ganze Code:
ViewController.h
@interface ViewController : UIViewController <UIPopoverControllerDelegate> @end
ViewController.m
@interface ViewController () @property BOOL isPopoverVisible; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; self.isPopoverVisible = NO; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // add validations here... self.isPopoverVisible = YES; [[(UIStoryboardPopoverSegue*)segue popoverController] setDelegate:self]; } - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { return !self.isPopoverVisible; } - (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController { self.isPopoverVisible = NO; } @end
quelle
Ich nahm Ricksters Antwort und packte sie in eine von UIViewController abgeleitete Klasse. Diese Lösung erfordert Folgendes:
Das Schöne daran ist, dass Sie keine "spezielle" Codierung vornehmen müssen, um den richtigen Umgang mit Popovers zu unterstützen.
Schnittstelle :
@interface FLStoryboardViewController : UIViewController { __strong NSString *m_segueIdentifier; __weak UIPopoverController *m_popoverController; } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender; - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender; @end
Implementierung :
@implementation FLStoryboardViewController - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if( [segue isKindOfClass:[UIStoryboardPopoverSegue class]] ) { UIStoryboardPopoverSegue *popoverSegue = (id)segue; if( m_popoverController == nil ) { assert( popoverSegue.identifier.length > 0 ); // The Popover segue should be named for this to work fully m_segueIdentifier = popoverSegue.identifier; m_popoverController = popoverSegue.popoverController; } else { [m_popoverController dismissPopoverAnimated:YES]; m_segueIdentifier = nil; m_popoverController = nil; } } else { [super prepareForSegue:segue sender:sender]; } } - (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender { // If this is an unnamed segue go ahead and allow it if( identifier.length != 0 ) { if( [identifier compare:m_segueIdentifier] == NSOrderedSame ) { if( m_popoverController == NULL ) { m_segueIdentifier = nil; return YES; } else { [m_popoverController dismissPopoverAnimated:YES]; m_segueIdentifier = nil; m_popoverController = nil; return NO; } } } return [super shouldPerformSegueWithIdentifier:identifier sender:sender]; } @end
Quelle auf GitHub verfügbar
quelle