UIRefreshControl без UITableViewController


308

Просто любопытно, так как это сразу не представляется возможным, но есть ли хитрый способ использовать новый UIRefreshControlкласс iOS 6 без использования UITableViewControllerподкласса?

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


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

3
@matt он должен использовать a UITableViewControllerи сообщить об ошибке, запрашивая API для непосредственного использования UIRefreshControla UITableView.
Дэйв Делонг

31
UITableViewController имел (и продолжает иметь) слишком много непонятных и нишевых ошибок и проблем с нетривиальными иерархиями представления ... которые волшебным образом исчезают, когда вы переключаетесь на использование стандартного VC с подпредставлением TableView. «Использование UITVC» - плохое начало для любого решения, ИМХО.
Адам

@ 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:)


РЕДАКТИРОВАТЬ: Это выше по-прежнему работает, но, как отмечают некоторые, есть небольшое «заикание» при добавлении 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
Вы действительно должны просто использовать представление контейнера для этого. Просто установите класс контроллера представления в свой собственный подкласс, UITableViewControllerи вы будете «делать все правильно».
Евгений

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

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

95

Чтобы устранить заикание, вызванное принятым ответом, вы можете присвоить свой UITableViewa 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. Сводит это до трех строк.
Тренскив

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

4
self.tableView = _tableViewController.tableView;должно быть_tableViewController.tableView = self.tableView
Али

@ Линус, почему это необходимо? Я добавил _tableViewController.view как subView в метод viewDidLoad моего пользовательского viewController, и это, похоже, помогло. Мне даже не нужно было устанавливать _tableViewController.tableView
Табер

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

21

То, что вы попробуете, это использовать представление контейнера внутри ViewController, который вы используете. Вы можете определить чистый подкласс UITableViewController с выделенным табличным представлением и поместить его в ViewController.


2
Точно, самый чистый способ - добавить дочерний контроллер вида типа UITableViewController.
Зденек

Затем вы можете получить UITableViewController с помощью self.childViewControllers.firstObject.
cbh2000

^ Вы также можете рассмотреть возможность использования prepareForSegueдля получения ссылки на объект UITableViewController в представлении контейнера, так как это немедленно запускается как событие segue при создании экземпляра из раскадровки.
Крахо

18

Ну, UIRefreshControl - это подкласс UIView, так что вы можете использовать его самостоятельно. Я не уверен, хотя, как это отрисовывает себя. Рендеринг может просто зависеть от фрейма, но он также может полагаться на UIScrollView или UITableViewController.

В любом случае, это будет скорее взлом, чем элегантное решение. Я рекомендую вам взглянуть на один из доступных сторонних клонов или написать свой собственный.

ODRefreshControl

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

SlimeRefresh

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


1
Спасибо за ваш ответ, но не совсем то, что я искал. Первое предложение вашего поста, однако, вдохновило меня попробовать то, что в итоге оказалось решением!
Келлер

7
ODRefreshControl потрясающий - спасибо за совет! Очень просто и делает свою работу. И, конечно, гораздо более гибкий, чем Apple. Я никогда не понимал, почему кто-то будет использовать UITableViewController, IMO худший класс во всем API. Он ничего не делает, но делает ваши взгляды негибкими.
13

Это на самом деле зависит от вашего проекта, что вы используете там ... использование uitableview в моем случае увеличивало сложность проекта, и теперь я переключился на uitableviewcontroller, который разделил мой код на два сегмента. Я рад, что у меня есть 2 файла поменьше, которые могут использоваться для отладки вместо одного огромного файла ..
kush

@ n13 Здравствуйте, хорошая причина использовать UITableViewController - возможность создавать статические ячейки. К сожалению, недоступно в tableView, находящемся в UIViewController.
Жан Ле Мойнан

@JeanLeMoignan Инструменты и библиотеки iOS быстро меняются, поэтому мой трехлетний комментарий больше не действителен. Я перешел на создание всех пользовательских интерфейсов в коде с помощью SnapKit, и, честно говоря, это намного эффективнее, чем нажатие 10 тысяч раз в конструкторе интерфейсов. Кроме того, изменение UITableViewController на UIViewController легко и занимает всего секунду, тогда как в IB это большая боль.
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

Я проверил это, и это также работает на коллекцию просмотров. Я заметил, что задержка в 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, поскольку 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, не вызывая super, могут быть некоторые вещи, на которые следует обратить внимание, и которые необходимо обработать при необходимости Например, установка tableview в моем примере выше не добавит 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, когда вкладка таблицы увеличивается после появления контроллера представления. Переход ко второму ответу с помощью контроллера uitableview позволил мне все исправить.


-6

Оказывается, вы можете использовать следующее при использовании UIViewController с подпредставлением UITableView и соответствуйте UITableViewDataSource и UITableViewDelegate:

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

9
Не согласен. self.refreshControlэто свойство для, а UITableViewControllerне UIViewController.
Рикстер

НЕ РАБОТАЕТ, referhControl не определен для uiviewcontroller
OMGPOP
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.