UIRefreshControl без UITableViewController


308

Цікаво, як це не відразу здається можливим, але чи є підлий спосіб використовувати новий UIRefreshControlклас iOS 6 без використання UITableViewControllerпідкласу?

Я часто використовую UIViewControllerз UITableViewпідвид і відповідати UITableViewDataSourceі , UITableViewDelegateа не з допомогою UITableViewControllerнаповал.


6
@DaveDeLong: Ні, Дейв, що йому робити? Пізніше на цій сторінці ви кажете, що його рішення не підтримується, тож правильне рішення?
мат

3
@ Matt він повинен використовувати UITableViewControllerі подати помилку запиту API використовувати UIRefreshControlз UITableViewбезпосередньо.
Дейв ДеЛонг

31
UITableViewController мав (і продовжує мати) занадто багато незрозумілих і нішевих помилок і проблем з нетривіальною ієрархією перегляду ..., що всі магічно зникають, коли ви переходите на використання стандартного VC з підпереглядом TableView. "Використовувати UITVC" - це поганий початок будь-якого рішення, IMHO.
Адам

@Adam Чи повертаються ці помилки під час використання "UITableViewController" як контролера дочірнього перегляду (таким чином надаючи доступ до налаштування перегляду, не замикаючись в ієрархії tableViewControllers)? Я ніколи не стикався з проблемами при використанні цього способу.
пам’ятки

Відповіді:


388

Під час переконання і на основі натхнення DrummerB я спробував просто додати UIRefreshControlекземпляр як підвид UITableView. І це магічно просто працює!

UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.myTableView addSubview:refreshControl];

Це додає UIRefreshControlвище подання таблиці і працює, як очікувалося, без використання UITableViewController:)


EDIT: Це вище все ще працює, але, як деякі з них зазначали, є незначне «заїкання» при додаванні UIRefreshControl таким чином. Рішенням цього є інстанціювати UITableViewController, а потім встановити для цього UIRefreshControl і UITableView, тобто:

UITableViewController *tableViewController = [[UITableViewController alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

48
БЮР, це непідтримувана поведінка і може змінитися в майбутньому. Єдина гарантія, яку дає Apple, - це те, що використання її відповідно до наданих api (в даному випадку -[UITableViewController setRefreshControl:]) буде продовжувати функціонувати.
Дейв ДеЛонг

18
З цією реалізацією здається, що безпосередньо перед запуском handleRefresh:відбудеться несподівана зміна значення перегляду прокрутки на частку contentInsetsсекунди. Хтось ще відчуває це чи маєте це виправити? (так, я знаю, що це в першу чергу не підтримується!)
Тім

6
Ви дійсно повинні просто використовувати Container View для цього. Просто встановіть клас контролера його перегляду на свій спеціальний підклас UITableViewControllerта ви будете "робити це правильно".
Євген

3
В iOS7 (невідомо для iOS6) відредагована версія працює добре, за винятком випадків, коли ви закриваєте та повторно відкриваєте додаток. Анімація не відтворюється до другого разу, коли ви оновите. Перше оновлення після повторного відкриття програми - це лише повне коло, а не коло, що збільшується, залежно від того, наскільки далеко ви його відтягуєте. Чи є виправлення?
david2391

30
Для того, щоб уникнути необхідності в «Слаттер» , як згадувалося вище , в той же час обходячи UITableViewController, просто додати елемент керування поновлення під табличному як так: [_tableView insertSubview:_refreshControl atIndex:0];. Тестували з iOS 7 та 8;)
ptitvinou

95

Щоб усунути заїкання, спричинене прийнятою відповіддю, ви можете призначити свій UITableViewа UITableViewController.

_tableViewController = [[UITableViewController alloc]initWithStyle:UITableViewStylePlain];
[self addChildViewController:_tableViewController];

_tableViewController.refreshControl = [UIRefreshControl new];
[_tableViewController.refreshControl addTarget:self action:@selector(loadStream) forControlEvents:UIControlEventValueChanged];

_theTableView = _tableViewController.tableView;

Редагувати:

Спосіб додавання " UIRefreshControlбез", UITableViewControllerбез заїкання та збереження приємної анімації після оновлення даних на перегляді таблиці.

UIRefreshControl *refreshControl = [UIRefreshControl new];
[refreshControl addTarget:self action:@selector(handleRefresh:) forControlEvents:UIControlEventValueChanged];
[self.theTableView addSubview:refreshControl];
[self.theTableView sendSubviewToBack:refreshControl];

Пізніше під час обробки оновлених даних ...

- (void)handleRefresh:(UIRefreshControl *)refreshControl {
    [self.theTableView reloadData];
    [self.theTableView layoutIfNeeded];
    [refreshControl endRefreshing];
}

7
Вам навіть не потрібно створювати новий UITableView. Просто скажіть initWithStyle: существующийTableView.style - а потім виконайте newTableViewController.tableView = существующийTableView і призначте refreshControl. Зводить це до трьох рядків.
Trenskow

Не забудьте зателефонувати [_tablewViewController didMoveToParentViewController:self];після додавання субпрогляду.
qix

4
self.tableView = _tableViewController.tableView;має бути_tableViewController.tableView = self.tableView
Алі

@Linus чому це потрібно? Я додав _tableViewController.view як subView в моєму власному методі viewDidLoad view viewController, і це, здавалося, зробило трюк. Мені навіть не потрібно було встановлювати _tableViewController.tableView
taber

Причина того, що я не можу користуватися UITableViewController і використовую UIViewController без перегляду таблиці всередині. stackoverflow.com/questions/18900428 / ...
lostintranslation

21

Що б ви спробували, це використовувати перегляд контейнера у ViewController, який ви використовуєте. Ви можете визначити чистий підклас UITableViewController із виділеним переглядом таблиці та розмістити його у ViewController.


2
Саме найчистішим способом, як видається, є додавання контролера подання дітей до типу UITableViewController.
Зденек

Потім ви можете захопити UITableViewController за допомогою self.childViewControllers.firstObject.
cbh2000

^ Ви також можете розглянути можливість використання prepareForSegueдля отримання посилання на об'єкт UITableViewController в режимі перегляду контейнера, оскільки це негайно спрацьовує як поважна подія при створенні інстанції з розгортки.
crarho

18

Добре UIRefreshControl - це підклас UIView, тому ви можете використовувати його самостійно. Я не впевнений, хоч як себе робить. Візуалізація може просто залежати від кадру, але також може покладатися на UIScrollView або UITableViewController.

Так чи інакше, це буде швидше хаком, ніж елегантним рішенням. Рекомендую заглянути в один із доступних сторонніх клонів або написати свій.

ODRefreshControl

введіть тут опис зображення

SlimeRefresh

введіть тут опис зображення


1
Дякую за вашу відповідь, але не саме те, що я шукав. Однак, перше речення вашого допису все-таки надихнуло мене спробувати те, що в кінцевому підсумку стало рішенням!
Келлер

7
ODRefreshControl - приголомшливий - дякую за пораду! Дуже просто, і робить роботу. І звичайно набагато гнучкіше, ніж Apple. Я ніколи не розумів, чому хтось використовуватиме UITableViewController, IMO - найгірший клас у всьому API. Це не робить нічого поруч, але робить ваші погляди негнучкими.
n13

Насправді залежить від вашого проекту, що ви використовуєте там. Використання uitableview в моєму випадку збільшувало складність проекту, і тепер я перейшов на uitableviewcontroller, який поділив свій код на два сегменти, я щасливий, що у мене є 2 менші файли, які можуть використовувати для налагодження замість одного величезного файлу ..
kush

@ n13 Привіт, вагомий привід використовувати UITableViewController - це можливість розробляти статичні комірки. На жаль, недоступний у tableView, що знаходиться в UIViewController.
Жан Ле Мойнан

@JeanLeMoignanІнструменти i бібліотеки для iOS швидко змінюються, тому трирічний коментар до мене вже не дійсний. Я перейшов на створення всіх користувальницьких інтерфейсів у коді за допомогою SnapKit, і, чесно кажучи, це набагато ефективніше, ніж натискання 10 тис. Разів у конструкторі інтерфейсів. Крім того, змінити UITableViewController на UIViewController легко і потрібно лише секунду, тоді як у ІБ це великий біль.
n13

7

Спробуйте затримати виклик -endRefreshметоду refreshControl на частку секунди після того, як tableView перезавантажить його вміст, використовуючи NSObject -performSelector:withObject:afterDelay:або GCD dispatch_after.

Для цього я створив категорію на UIRefreshControl:

@implementation UIRefreshControl (Delay)

- (void)endRefreshingAfterDelay:(NSTimeInterval)delay {
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self endRefreshing];
    });
}

@end

Я перевірив це, і це також працює на Collection Views. Я помітив, що затримки достатньо всього 0,01 секунди:

// My data refresh process here while the refresh control 'isRefreshing'
[self.tableView reloadData];
[self.refreshControl endRefreshingAfterDelay:.01];

4
Затримки та очікування - це справді поганий стиль.
orkoden

2
@orkoden Я згоден. Це рішення для вже непідтримуваного використання UIRefreshControl.
болива

Ви можете зателефонувати за методом із затримкою набагато простіше. [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
orkoden

2
Кінцевий ефект точно такий же, і не робить «злом» більш / менш елегантним. Чи використовувати -performSelector:withObject:afterDelay:для цього чи GCD - це питання особистого смаку.
болива

7

IOS 10 Swift 3.0

це просто

import UIKit

class ViewControllerA: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var myTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        myTableView.delegate = self
        myTableView.dataSource = self

        if #available(iOS 10.0, *) {
            let refreshControl = UIRefreshControl()
            let title = NSLocalizedString("PullToRefresh", comment: "Pull to refresh")
            refreshControl.attributedTitle = NSAttributedString(string: title)
            refreshControl.addTarget(self,
                                     action: #selector(refreshOptions(sender:)),
                                     for: .valueChanged)
            myTableView.refreshControl = refreshControl
        }
    }

    @objc private func refreshOptions(sender: UIRefreshControl) {
        // Perform actions to refresh the content
        // ...
        // and then dismiss the control
        sender.endRefreshing()
    }

    // MARK: - Table view data source

    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 12
    }


    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "reuseIdentifier", for: indexPath)

        cell.textLabel?.text = "Cell \(String(indexPath.row))"
        return cell
    }

}

введіть тут опис зображення

Якщо ви хочете дізнатися про iOS 10 UIRefreshControl, читайте тут .


3

Додавання керування оновленням як підвід, створює порожнє місце над заголовками розділів.

Натомість я вбудував UITableViewController у свій UIViewController, а потім змінив свою tableViewвласність, щоб вказати на вбудований та віолу! Мінімальні зміни коду. :-)

Кроки:

  1. Створіть новий UITableViewController в Інструментарій та вставте його у свій оригінальний UIViewController
  2. Замініть @IBOutlet weak var tableView: UITableView!його на щойно вбудований UITableViewController, як показано нижче

class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    weak var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let tableViewController = self.childViewControllers.first as! UITableViewController
        tableView = tableViewController.tableView
        tableView.dataSource = self
        tableView.delegate = self

        // Now we can (properly) add the refresh control
        let refreshControl = UIRefreshControl()
        refreshControl.addTarget(self, action: "handleRefresh:", forControlEvents: .ValueChanged)
        tableViewController.refreshControl = refreshControl
    }

    ...
}

2

Для Swift 2.2.

Спочатку зробіть UIRefreshControl ().

var refreshControl : UIRefreshControl!

У вашому методі viewDidLoad () додайте:

refreshControl = UIRefreshControl()
    refreshControl.attributedTitle = NSAttributedString(string: "Refreshing..")
    refreshControl.addTarget(self, action: #selector(YourUIViewController.refresh(_:)), forControlEvents: UIControlEvents.ValueChanged)
    self.tableView.addSubview(refreshControl)

І зробити функцію оновлення

func refresh(refreshControl: UIRefreshControl) {

    // do something ...

    // reload tableView
    self.tableView.reloadData()

    // End refreshing
    refreshControl.endRefreshing()
}

0

Ось ще одне рішення, яке трохи відрізняється.

Мені довелося використовувати його через деякі проблеми з ієрархією перегляду: я створив певну функціональність, яка вимагала перегляду представлень навколо різних місць в ієрархії представлення даних, які порушуються при використанні набору таблиць UITableViewController b / c, а tableView - кореневий вигляд UITableViewController ( self.view), а не просто звичайний перегляд, він створив непослідовну ієрархію контролера / перегляду та спричинив збій.

В основному створіть свій власний підклас UITableViewController та замініть loadView, щоб призначити self.view інший вигляд, і замініть властивість tableView, щоб повернути окремий перегляд таблиці.

наприклад:

@interface MyTableVC : UITableViewController
@end

@interface MyTableVC ()
@property (nonatomic, strong) UITableView *separateTableView;
@end

@implementation MyTableVC

- (void)loadView {
    self.view = [[UIView alloc] initWithFrame:CGRectZero];
}

- (UITableView *)tableView {
    return self.separateTableView;
}

- (void)setTableView:(UITableView *)tableView {
    self.separateTableView = tableView;
}

@end

У поєднанні з рішенням Келлера це стане більш надійним у тому сенсі, що tableView тепер є звичайним виглядом, а не кореневим видом VC, і буде більш надійним щодо зміни ієрархій перегляду. Приклад використання цього способу:

MyTableVC *tableViewController = [[MyTableVC alloc] init];
tableViewController.tableView = self.myTableView;

self.refreshControl = [[UIRefreshControl alloc] init];
[self.refreshControl addTarget:self action:@selector(getConnections) forControlEvents:UIControlEventValueChanged];
tableViewController.refreshControl = self.refreshControl;

Для цього є ще одне можливе використання:

Оскільки підкласифікація таким чином відокремлює self.view від self.tableView, тепер можна використовувати цей UITableViewController як більше звичайного контролера та додавати інші підпогляди до self.view без дивацтв додавання підвідданих до UITableView, тож можна розглянути можливість їх створення переглядати контролери безпосередньо підкласу UITableViewController замість того, щоб мати дітей UITableViewController.

Деякі речі, на які слід звернути увагу:

Оскільки ми переосмислюємо властивість tableView без виклику супер, можливо, слід спостерігати за деякими речами, які слід обробляти, де це необхідно. Наприклад, встановлення перегляду таблиці в моєму вище прикладі не додасть перегляду таблиці в self.view і не встановить кадр, який ви можете зробити. Крім того, у цій реалізації відсутня таблиця за замовчуванням, надана вам, коли клас інстанціюється, що також ви можете розглянути, як додати. Я не включаю його сюди, тому що це є конкретним випадком, і це рішення насправді добре відповідає рішенням Келлера.


1
Питання вказано "без використання підкласу UITableViewController", і ця відповідь стосується підкласу UITableViewController.
Брайан

0

Спробуйте це,

Вищевказані рішення чудові, але tableView.refreshControl доступний лише для UITableViewController, до iOS 9.x та постачається в UITableView від iOS 10.x і далі.

Написано у Swift 3 -

let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(FeedViewController.loadNewData), for: UIControlEvents.valueChanged)
// Fix for the RefreshControl Not appearing in background
tableView?.addSubview(refreshControl)
tableView.sendSubview(toBack: refreshControl)

Найкраще рішення поки що
Karthik KM

-1

Перша пропозиція Келлера викликає дивну помилку в iOS 7, де набір таблиці збільшується після того, як знову з’явиться контролер перегляду. Змінившись до другої відповіді, використовуючи uitableviewcontroller, виправляв для мене речі.


-6

Виявляється, ви можете використовувати наступне, коли ви використовуєте UIViewController з підрозглядом UITableView та відповідати UITableViewDataSource та UITableViewDelegate:

self.refreshControl = [[UIRefreshControl alloc]init];
[self.refreshControl addTarget:self action:@selector(refresh:) forControlEvents:UIControlEventValueChanged];

9
Не погоджуюсь. self.refreshControlє властивістю для a UITableViewController, а не a UIViewController.
Рікстер

НЕ ПРАЦЮЄ, referencehControl не визначено для uiviewcontroller
OMGPOP
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.