developer tip

스토리 보드 팝 오버를 닫는 방법

optionbox 2020. 10. 24. 10:01
반응형

스토리 보드 팝 오버를 닫는 방법


다음 UIBarButtonItem과 같이 Xcode Storyboards를 사용하여 팝 오버를 만들었습니다 (코드가 없음).

Popover가있는 Xcode 5.0 Connections Inspector

팝 오버를 표시하면 잘 작동합니다. 그러나 팝업을 표시 것을 탭하면 팝 오버가 사라지 도록 할 수 없습니다 UIBarButtonItem.

버튼을 처음 누르면 (처음) 팝 오버가 나타납니다. 버튼을 다시 누르면 (두 번째) 동일한 팝 오버가 그 위에 나타나므로 이제 두 개의 팝 오버가 생깁니다 (또는 버튼을 계속 누르면 그 이상). iOS 휴먼 인터페이스 지침 에 따르면 팝 오버가 첫 번째 탭에서 나타나고 두 번째 탭에서 사라지도록해야합니다.

한 번에 하나의 팝 오버 만 화면에 표시되는지 확인하십시오. 동시에 두 개 이상의 팝 오버 (또는 팝 오버처럼 보이고 동작하도록 설계된 사용자 정의보기)를 표시해서는 안됩니다. 특히, 하나의 팝 오버가 다른 팝 오버에서 나오는 캐스케이드 또는 계층의 팝 오버를 동시에 표시하지 않아야합니다.

사용자 UIBarButtonItem가 두 번째 로을 탭할 때 팝 오버를 해제하려면 어떻게해야합니까?


편집 : 이러한 문제는 iOS 7.1 / Xcode 5.1.1에서 수정 된 것으로 보입니다. (아마도 이전에는 모든 버전을 테스트 할 수 없었습니다. 확실히 iOS 7.0 이후 버전을 테스트 한 이후입니다.)에서 팝 오버 segue를 만들 때 UIBarButtonItemsegue는 팝 오버를 다시 탭하면 팝 오버가 오히려 숨겨집니다. 중복을 보여주는 것보다. UIPresentationControllerXcode 6이 iOS 8 용으로 생성 하는 새로운 기반 팝 오버 세그먼트에도 적합합니다.

내 솔루션은 여전히 ​​이전 iOS 버전을 지원하는 사람들에게 역사적인 관심사가 될 수 있으므로 아래에 남겨 두었습니다.


segue의 팝 오버 컨트롤러에 대한 참조를 저장하고을 반복 호출 할 때 새 값으로 설정하기 전에이를 prepareForSegue:sender:닫으면 피할 수있는 것은 버튼을 반복해서 누를 때 여러 스택 팝 오버 가 발생하는 문제입니다. 여전히 사용할 수 없습니다. HIG가 권장하는 팝 오버를 닫는 버튼 (및 Apple의 앱 등에 표시됨)

하지만 간단한 솔루션을 위해 ARC 제로화 약한 참조를 활용할 수 있습니다.

1 : 버튼에서 Segue

iOS 5부터는에서 segue로이 작업을 수행 UIBarButtonItem할 수 없지만 iOS 6 이상에서는 가능합니다. (iOS 5에서는 뷰 컨트롤러 자체에서 분리 한 다음 performSegueWithIdentifier:팝 오버를 확인한 후 버튼의 액션 호출을 받아야합니다 .)

2 : 팝 오버에 대한 참조 사용 -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 : 3 단계가 없습니다!

여기서 제로화 약한 참조를 사용하는 것에 대한 좋은 점은 일단 팝 오버 컨트롤러가 해제되면 (프로그래밍 방식으로에서든 shouldPerformSegueWithIdentifier:, 사용자가 팝 오버 외부의 다른 곳을 탭하여 자동으로)-ivar가 nil다시 이동 하므로 우리는 다시 초기 상태.

약한 참조를 제로화하지 않으면 다음 작업도 수행해야합니다.

  • 에서 해제 myPopover = nil할 때 설정 shouldPerformSegueWithIdentifier:하고
  • 잡기 위해 우리 자신을 팝 오버 컨트롤러의 델리게이트로 popoverControllerDidDismissPopover:설정하고 myPopover = nil거기 에서도 설정 합니다 (팝 오버가 자동으로 닫힐 때 잡습니다).

나는 여기에 해결책을 발견 https://stackoverflow.com/a/7938513/665396 첫 prepareForSegue에서 : 보낸 사람 : 저장소를 바르 / 속성하여 UIPopoverController에 대한 포인터와 포인터가 후속 호출에서 팝 오버를 기각 할 것을 사용자.

...
@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;
...
}

나는 이것을 위해 사용자 정의 segue를 사용했습니다.

1

Storyboard에서 사용할 사용자 지정 segue를 만듭니다.

@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

segue의 소스 / 입력 인 뷰 컨트롤러에서 예를 들어 액션으로 segue를 시작합니다.

-(void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar
{
    if(nil == self.recentSearchesPopoverController)
    {
        NSString *identifier = NSStringFromClass([CustomPopoverSegue class]);
        [self performSegueWithIdentifier:identifier sender:self];
    } 
}

참조는 UIPopoverController를 생성하는 segue에 의해 할당됩니다-팝 오버를 닫을 때

-(void)searchBarTextDidEndEditing:(UISearchBar *)searchBar
{
    if(self.recentSearchesPopoverController)
    {
        [self.recentSearchesPopoverController dismissPopoverAnimated:YES];
        self.recentSearchesPopoverController = nil;
    }    
}

안부, 피터


ixPopoverBarButtonItemsegue를 트리거하거나 표시되는 팝 오버를 닫는 사용자 지정 생성하여 해결했습니다 .

내가하는 일 : 버튼의 동작과 대상을 토글하여 segue를 트리거하거나 현재 표시된 팝 오버를 삭제합니다.

이 솔루션을 찾기 위해 많은 인터넷 검색이 필요했습니다. 액션을 토글하는 아이디어에 대한 크레딧을 받고 싶지 않습니다. 코드를 사용자 지정 버튼에 넣는 것은 내 관점에서 상용구 코드를 최소한으로 유지하려는 접근 방식이었습니다.

스토리 보드에서 BarButtonItem의 클래스를 사용자 지정 클래스에 정의합니다.

맞춤 막대 버튼

그런 다음 segue에서 만든 팝 오버를 prepareForSegue:sender:메서드의 사용자 지정 단추 구현에 전달합니다 .

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender  
{
    if ([segue.identifier isEqualToString:@"myPopoverSegue"]) {
        UIStoryboardPopoverSegue* popSegue = (UIStoryboardPopoverSegue*)segue;
        [(ixPopoverBarButtonItem *)sender showingPopover:popSegue.popoverController];
    }
}

Btw ... 팝 오버를 트리거하는 버튼이 두 개 이상 있기 때문에 현재 표시된 팝 오버에 대한 참조를 유지하고 새 팝 오버를 표시 할 때이를 닫아야합니다. 그러나 이것은 귀하의 질문이 아닙니다 ...

사용자 지정 UIBarButtonItem을 구현 한 방법은 다음과 같습니다.

...상호 작용:

@interface ixPopoverBarButtonItem : UIBarButtonItem

- (void) showingPopover:  (UIPopoverController *)popoverController;

@end

... 그리고 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

추신 : 저는 ARC를 처음 사용하기 때문에 여기에서 유출되고 있는지 완전히 확신하지 못합니다. 내가 만약 ...


.NET Framework의 복사본을 보관할 필요없이이 문제를 해결했습니다 UIPopoverController. 스토리 보드 (Toolbar, BarButtons 등)의 모든 것을 처리하고

  • 부울로 팝 오버의 가시성을 처리합니다.
  • 대리자가 있는지 확인하고 self로 설정되어 있습니다.

다음은 모든 코드입니다.

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

나는 rickster의 대답을 가져 와서 UIViewController에서 파생 된 클래스로 패키지했습니다. 이 솔루션에는 다음이 필요합니다.

  • ARC가있는 iOS 6 이상
  • 이 클래스에서 뷰 컨트롤러를 가져옵니다.
  • 이러한 메서드를 재정의하는 경우 prepareForSegue : sender 및 shouldPerformSegueWithIdentifier : sender의 "수퍼"버전을 호출해야합니다.
  • 명명 된 팝 오버 segue 사용

이것의 좋은 점은 Popover의 적절한 처리를 지원하기 위해 "특별한"코딩을 할 필요가 없다는 것입니다.

인터페이스 :

@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

구현 :

@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

GitHub에서 사용 가능한 소스

참고 URL : https://stackoverflow.com/questions/8287242/how-to-dismiss-a-storyboard-popover

반응형