UIRefreshControl-UITableViewController가 UINavigationController 내부에있을 때 beginRefreshing이 작동하지 않음


120

내 UITableViewController (UINavigationController 내부에 있음)에 UIRefreshControl을 설정했으며 예상대로 작동합니다 (즉, 풀다운으로 올바른 이벤트가 발생 함). 그러나 beginRefreshing다음과 같이 새로 고침 컨트롤 에서 인스턴스 메서드를 프로그래밍 방식으로 호출하면

[self.refreshControl beginRefreshing];

아무 반응이 없습니다. 아래로 애니메이션되고 스피너가 표시되어야합니다. endRefreshing내가 새로 고침 후 그를 호출 할 때 방법은 제대로 작동합니다.

이 동작으로 기본 프로토 타입 프로젝트를 채찍질했으며 UITableViewController가 애플리케이션 델리게이트의 루트 뷰 컨트롤러에 직접 추가 될 때 제대로 작동합니다. 예 :

self.viewController = tableViewController;
self.window.rootViewController = self.viewController;

그러나 tableViewController먼저 UINavigationController에를 추가 한 다음 탐색 컨트롤러를으로 추가 rootViewController하면 beginRefreshing메서드가 더 이상 작동하지 않습니다. 예

UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:tableViewController];
self.viewController = navController;
self.window.rootViewController = self.viewController;

내 느낌은 이것이 새로 고침 컨트롤과 잘 어울리지 않는 탐색 컨트롤러 내의 중첩 된 뷰 계층과 관련이 있다는 것입니다-제안 사항이 있습니까?

감사

답변:


195

프로그래밍 방식으로 새로 고침을 시작하면 contentoffset을 변경하여 테이블보기를 직접 스크롤해야하는 것 같습니다.

[self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];

그 이유는 사용자가 테이블보기의 중간 / 하단에있을 때 새로 고침 컨트롤로 스크롤하는 것이 바람직하지 않을 수 있기 때문이라고 생각합니다.

@muhasturk의 Swift 2.2 버전

self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)

간단히 말해서,이 휴대 성을 유지하려면이 확장을 추가하십시오.

UIRefreshControl + ProgramaticallyBeginRefresh.swift

extension UIRefreshControl {
    func programaticallyBeginRefreshing(in tableView: UITableView) {
        beginRefreshing()
        let offsetPoint = CGPoint.init(x: 0, y: -frame.size.height)
        tableView.setContentOffset(offsetPoint, animated: true)        
    }
}

6
즉 필요에 따라 endRefreshing가 조정 오프셋, 내 시험에서, 이상하다
드미트리 셰브첸코를

9
BTW, 자동 레이아웃을 사용하는 경우 대답의 줄을 다음과 같이 바꿀 수 있습니다. [self.tableView setContentOffset : CGPointMake (0, -self.topLayoutGuide.length) animated : YES];
Eric Baker

4
@EricBaker 나는 그렇게하지 않을 것이라고 믿습니다. 모든 UITableViewController가 탐색 모음을 표시하는 것은 아닙니다. 이로 인해 topLayoutGuide 길이가 20이고 오프셋이 너무 작습니다.
Fábio Oliveira

3
@EricBaker 사용할 수 있습니다 : [self.tableView setContentOffset : CGPointMake (0, self.topLayoutGuide.length -self.refreshControl.frame.size.height) animated : YES];
ZYiOS 2014 년

1
복사 붙여 넣기가 저에게 효과적입니다. 나는 애플의 스토리 보드 빌더에 질렸다.
okysabeni 2015 년

78

UITableViewController는 iOS 7 이후 자동으로 AdjustsScrollViewInsets 속성을 갖습니다. 테이블보기에는 이미 contentOffset (일반적으로 0, -64)이있을 수 있습니다.

따라서 프로그래밍 방식으로 새로 고침을 시작한 후 refreshControl을 표시하는 올바른 방법은 기존 contentOffset에 refreshControl의 높이를 추가하는 것입니다.

 [self.refreshControl beginRefreshing];
 [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];

안녕하세요, 고맙습니다. 디버깅 할 때 만난 매직 포인트 (0, -64)도 궁금합니다.
inix 2015-04-21

2
상태 표시 줄 높이의 경우 @inix 20 + 탐색 표시 줄 높이의 경우 44
Igotit 2015

1
대신 사용하는 -64것이 더 좋습니다-self.topLayoutGuide.length
Diogo T

33

다음은 위에서 설명한 전략을 사용하는 Swift 확장입니다.

extension UIRefreshControl {
    func beginRefreshingManually() {
        if let scrollView = superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: true)
        }
        beginRefreshing()
    }
}

UITableView에서 작동합니다.
Juan Boero

10
sendActionsForControlEvents(UIControlEvents.ValueChanged)이 함수의 끝에 두는 것이 좋습니다 . 그렇지 않으면 실제 새로 고침 논리 논리가 실행되지 않습니다.
Colin Basnett

이것은 그것을 수행하는 가장 우아한 방법입니다. 더 나은 기능에 대한 Colin Basnett의 의견 포함. 한 번 정의하면 전체 프로젝트에서 사용할 수 있습니다!
JoeGalind

1
@ColinBasnett : 코드를 추가하면 (이제 sendActions (for : UIControlEvents.valueChanged)) 무한 루프가 발생합니다 ...
Kendall Helmstetter Gelner

15

다른 답변 중 어느 것도 나를 위해 일하지 않았습니다. 스피너가 표시되고 회전하도록하지만 새로 고침 동작 자체는 발생하지 않습니다. 이것은 작동합니다 :

id target = self;
SEL selector = @selector(example);
// Assuming at some point prior to triggering the refresh, you call the following line:
[self.refreshControl addTarget:target action:selector forControlEvents:UIControlEventValueChanged];

// This line makes the spinner start spinning
[self.refreshControl beginRefreshing];
// This line makes the spinner visible by pushing the table view/collection view down
[self.tableView setContentOffset:CGPointMake(0, -1.0f * self.refreshControl.frame.size.height) animated:YES];
// This line is what actually triggers the refresh action/selector
[self.refreshControl sendActionsForControlEvents:UIControlEventValueChanged];

이 예제는 테이블보기를 사용하지만 콜렉션보기 일 수도 있습니다.


이것은 내 문제를 해결했습니다. 하지만 지금은을 사용 SVPullToRefresh하고 있습니다. 어떻게 프로그래밍 방식으로 끌어 내릴까요?
Gank

1
@Gank 저는 SVPullToRefresh를 사용한 적이 없습니다. 그들의 문서를 읽어 보셨습니까? 문서에 따르면 프로그래밍 방식으로 풀다운 할 수 있다는 것이 매우 분명해 보입니다. "프로그래밍 방식으로 새로 고침을 트리거하려면 (예 : viewDidAppear :) 다음을 사용하여 수행 할 수 있습니다. [tableView triggerPullToRefresh];"참조 : github.com/samvermette/ SVPullToRefresh
Kyle Robson

1
완전한! 나는 단순히 그것을 대체 그래서, 비록 애니메이션 나를 위해 이상한 행동을했다scrollView.contentOffset = CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height)
user1366265

13

이미 언급 한 접근 방식 :

[self.refreshControl beginRefreshing];
 [self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];

스피너를 볼 수 있습니다. 그러나 그것은 움직이지 않을 것입니다. 내가 변경 한 한 가지는이 두 가지 방법의 순서이며 모든 것이 작동했습니다.

[self.tableView setContentOffset:CGPointMake(0, self.tableView.contentOffset.y-self.refreshControl.frame.size.height) animated:YES];
[self.refreshControl beginRefreshing];

11

Swift 4 / 4.1의 경우

기존 답변의 혼합이 나를 위해 일합니다.

refreshControl.beginRefreshing()
tableView.setContentOffset(CGPoint(x: 0, y: tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)

도움이 되었기를 바랍니다!


7

이 질문도 참조

beginRefreshing을 호출하고 contentOffset이 0 일 때 UIRefreshControl이 가시적으로 표시되지 않음

tableView의 contentOffset 속성이 0 일 때만 발생하기 때문에 버그처럼 보입니다.

다음 코드 (UITableViewController 메서드)로 수정했습니다.

- (void)beginRefreshingTableView {

    [self.refreshControl beginRefreshing];

    if (self.tableView.contentOffset.y == 0) {

        [UIView animateWithDuration:0.25 delay:0 options:UIViewAnimationOptionBeginFromCurrentState animations:^(void){

            self.tableView.contentOffset = CGPointMake(0, -self.refreshControl.frame.size.height);

        } completion:^(BOOL finished){

        }];

    }
}

1
이것은 사실이 아닙니다. 두 개의 서로 다른 UIViewController가 있습니다. 둘 다 viewDidLoad에서 contentOffset이 0이고 그 중 하나는 [self.refreshControl beginRefreshing]을 호출 할 때 refreshControl을 올바르게 풀다운하고 다른 하나는 그렇지 않습니다. : /
simonthumper

문서는에 컨트롤을 표시하는 것에 대해 아무것도 말하지 않고 beginRefreshing상태가 변경된다는 것입니다. 내가 볼 수 있듯이 새로 고침 작업을 두 번 시작하지 않도록 프로그래밍 방식으로 호출 된 새로 고침이 계속 실행되고 사용자가 시작한 작업이 다른 작업을 시작하지 않을 수 있습니다.
Koen.

setContentOffset : animated 메서드 사용과 관련된 문제를 지적 했으므로이 솔루션이 저에게 효과적이었습니다.
Marc Etcheverry

7

다음은 스피너를 표시하고 애니메이션을 적용하는 Swift 3 이상 확장입니다.

import UIKit
extension UIRefreshControl {

func beginRefreshingWithAnimation() {

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {

        if let scrollView = self.superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - self.frame.height), animated: true)
          }
        self.beginRefreshing()
      }
   }
}

2
위의 모든 답변이 내가 11.1 / 엑스 코드 9.1 iOS에서 작업을 얻을 수있는 유일한 사람이었다
Bassebus

1
asyncAfter(10.2 아이폰 OS 12.3 / 엑스 코드)를 스피너 애니메이션 작품을 만드는 것 실제로
francybiga

4

나에게 완벽하게 작동합니다.

스위프트 3 :

self.tableView.setContentOffset(CGPoint(x: 0, y: -self.refreshControl!.frame.size.height - self.topLayoutGuide.length), animated: true)

4

Swift 5의 경우 나에게 빠진 유일한 것은 refreshControl.sendActions(.valueChanged). 나는 그것을 더 깨끗하게 만들기 위해 확장했습니다.

extension UIRefreshControl {

    func beginRefreshingManually() {
        if let scrollView = superview as? UIScrollView {
            scrollView.setContentOffset(CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height), animated: false)
        }
        beginRefreshing()
        sendActions(for: .valueChanged)
    }

}

3

@Dymitry Shevchenko 솔루션 외에도.

이 문제에 대한 좋은 해결 방법을 찾았습니다. UIRefreshControl해당 덮어 쓰기 메서드에 대한 확장을 만들 수 있습니다 .

// Adds code forgotten by Apple, that changes content offset of parent scroll view (table view).
- (void)beginRefreshing
{
    [super beginRefreshing];

    if ([self.superview isKindOfClass:[UIScrollView class]]) {
        UIScrollView *view = (UIScrollView *)self.superview;
        [view setContentOffset:CGPointMake(0, view.contentOffset.y - self.frame.size.height) animated:YES];
    }
}

Interface Builder에서 새로 고침 제어를 위해 Identity Inspector 에서 사용자 정의 클래스를 설정하여 새 클래스를 사용할 수 있습니다 .


3

Fort Swift 2.2 이상

    self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.frame.size.height), animated: true)

나는 단지 업데이트이기 때문에 이것을 받아 들인 대답과 병합했습니다.
Tudor

3

swift 3.1에 Rxswift를 사용하는 경우 다음을 사용할 수 있습니다.

func manualRefresh() {
    if let refreshControl = self.tableView.refreshControl {
        self.tableView.setContentOffset(CGPoint(x: 0, y: -refreshControl.height), animated: true)
        self.tableView.refreshControl?.beginRefreshing()
        self.tableView.refreshControl?.sendActions(for: .valueChanged)
    }
}

이것은 신속한 3.1, iOS 10에서 작동합니다.


1
그것 sendActions트리거 rx관련이 대답하게 RxSwift넣다 누군가 먼저 살펴에 궁금해
carbonr

tableView가 아닌 ​​tableViewController의 refreshControl 인스턴스를 사용해야했습니다. 또한 그 setContentOffset은 iOS10을 대상으로하는 나를 위해 작동하지 않았습니다. 이 사람은 그러나 작동 : self.tableView.setContentOffset(CGPoint(x:0, y:self.tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)
nmdias에게

1

Swift 5에서 테스트

이것을 사용하십시오 viewDidLoad()

fileprivate func showRefreshLoader() {
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) {
        self.tableView.setContentOffset(CGPoint(x: 0, y: self.tableView.contentOffset.y - (self.refreshControl.frame.size.height)), animated: true)
        self.refreshControl.beginRefreshing() 
    }
}

0

사용자 "데이터가 업데이트 됨"시각적 기호 표시에 동일한 기술을 사용합니다. 결과 사용자가 백그라운드에서 앱을 가져오고 피드 / 목록은 사용자가 자신을 새로 고치기 위해 테이블을 끌어 오는 것과 같은 UI로 업데이트됩니다. 내 버전에는 3 가지가 포함되어 있습니다.

1) "깨우기"를 보내는 사람

- (void)applicationDidBecomeActive:(UIApplication *)application {
    [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationHaveToResetAllPages object:nil];
}

2) UIViewController의 관찰자

- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(forceUpdateData) name:kNotificationHaveToWakeUp:nil];
}

3) 프로토콜

#pragma mark - ForcedDataUpdateProtocol

- (void)forceUpdateData {
    self.tableView.contentOffset = CGPointZero;

    if (self.refreshControl) {
        [self.refreshControl beginRefreshing];
        [self.tableView setContentOffset:CGPointMake(0, -self.refreshControl.frame.size.height) animated:YES];
        [self.refreshControl performSelector:@selector(endRefreshing) withObject:nil afterDelay:1];
    }
}

결과

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.