it-roy-ru.com

UIRefreshControl - beginRefreshing не работает, когда UITableViewController находится внутри UINavigationController

Я установил UIRefreshControl в своем UITableViewController (который находится внутри UINavigationController), и он работает, как и ожидалось (то есть, выпадающее меню запускает правильное событие). Однако, если я программно вызываю метод экземпляра 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;

Я чувствую, что это как-то связано с вложенными иерархиями представлений в контроллере навигации, которые не воспроизводят «Ниццу» с помощью элемента управления переподготовкой.

Спасибо 

105
wows

Кажется, что если вы начнете обновлять программно, вам придется прокрутить табличное представление самостоятельно, скажем, изменив contentoffset

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

Я думаю, причина этого в том, что может быть нежелательно переходить к элементу управления обновлением, когда пользователь находится в середине/внизу табличного представления?

Версия Swift 2.2 от @muhasturk

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

В двух словах, чтобы сохранить этот портативный добавить это расширение

UIRefreshControl + PrograicallyBeginRefresh.Swift

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

UITableViewController имеет automaticAdjustsScrollViewInsets свойство после iOS 7. В табличном представлении уже может быть contentOffset, обычно (0, -64).

Таким образом, правильный способ показать refreshControl после программного обновления - добавить высоту refreshControl к существующему contentOffset.

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

Вот расширение 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()
    }
}
27
Chris Wagner

Ни один из других ответов не работал для меня. Они заставили бы вращаться, чтобы показать и вращаться, но само действие обновления никогда не произойдет. Это работает:

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

Обратите внимание, что в этом примере используется табличное представление, но оно также может быть представлением коллекции.

12
Kyle Robson

Уже упомянутый подход:

[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
ancajic

Смотрите также этот вопрос 

UIRefreshControl не показывает spiny при вызове beginRefreshing и contentOffset равен 0

Для меня это выглядит как ошибка, потому что это происходит только тогда, когда свойство contentOffset tableView равно 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){

        }];

    }
}
7
Peter Lapisu

Для Swift 4/4.1

Смесь существующих ответов сделает всю работу за меня:

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

Надеюсь это поможет!

5
Isaac Bosca

Это прекрасно работает для меня:

Свифт 3:

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

Вот расширение 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()
      }
   }
}
4
Ghulam Rasool

В дополнение к решению @Dymitry Шевченко.

Я нашел хороший обходной путь к этой проблеме. Вы можете создать расширение для 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];
    }
}

Вы можете использовать новый класс, установив пользовательский класс в Identity Inspector для управления обновлением в Интерфейсном Разработчике.

3
lyzkov

Форт Свифт 2.2+

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

Если вы используете Rxswift для Swift 3.1, можете использовать ниже:

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)
    }
}

Эта работа для Swift 3.1, iOS 10.

3
jkyin

Для 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)
    }

}
0
LukasHromadnik

Я использую ту же технику для показа пользователю "data is update" визуального знака. В результате пользователь выводит приложение из фона, а фиды/списки будут обновляться с помощью пользовательского интерфейса, подобно тому, как пользователи извлекают таблицы для обновления. Моя версия содержит 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];
    }
}

 result

0
WINSergey