UIRefreshControl - UITableViewController, UINavigationController içinde olduğunda beginRefreshing çalışmıyor


120

UITableViewController'ımda (bir UINavigationController'ın içinde) bir UIRefreshControl kurdum ve beklendiği gibi çalışıyor (yani aşağı çekme doğru olayı tetikliyor). Ancak, beginRefreshingyenileme denetiminde örnek yöntemini programlı olarak çağırırsam şunun gibi:

[self.refreshControl beginRefreshing];

Hiçbir şey olmuyor. Canlandırmalı ve çarkı göstermelidir. endRefreshingBen yenilemeden sonra o çağırdığınızda yöntemi düzgün çalışıyor.

Bu davranışla temel bir prototip projesi hazırladım ve UITableViewController'ım doğrudan uygulama temsilcisinin kök görünüm denetleyicisine eklendiğinde düzgün çalışıyor, örneğin:

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

Ama tableViewControllerönce bir UINavigationController'a eklersem, sonra gezinti denetleyicisini eklersem rootViewController, beginRefreshingyöntem artık çalışmıyor. Örneğin

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

Bence bunun, tazeleme kontrolüyle iyi oynamaması için gezinme denetleyicisindeki iç içe geçmiş görünüm hiyerarşileriyle bir ilgisi var - herhangi bir öneriniz var mı?

Teşekkürler

Yanıtlar:


195

Görünüşe göre programlı olarak yenilemeye başlarsanız, örneğin contentoffset'i değiştirerek tablo görünümünü kendiniz kaydırmanız gerekir.

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

Sanırım bunun nedeni, kullanıcı tablo görünümünün ortasındayken / altındayken yenileme kontrolüne gitmenin istenmeyen bir durum olabileceğidir.

@Muhasturk tarafından Swift 2.2 sürümü

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

Özetle, bu taşınabilirliği korumak için bu uzantıyı ekleyin

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
Tuhaf, testlerimde son Yenileme ofseti gerektiği gibi ayarlar
Dmitry Shevchenko

9
BTW, otomatik düzen kullanıyorsanız yanıttaki satırı şununla değiştirebilirsiniz: [self.tableView setContentOffset: CGPointMake (0, -self.topLayoutGuide.length) animated: YES];
Eric Baker

4
@EricBaker Bunun işe yaramayacağına inanıyorum. Tüm UITableViewControllers, gezinme çubuklarını göstermez. Bu, 20 uzunluğunda bir topLayoutGuide ve çok küçük bir kaymaya yol açar.
Fábio Oliveira

3
@EricBaker şunları kullanabilirsiniz: [self.tableView setContentOffset: CGPointMake (0, self.topLayoutGuide.length -self.refreshControl.frame.size.height) animasyonlu: YES];
ZYiOS

1
Kopyala yapıştır benim için çalışıyor ki bu harika. Apple'ın film şeridi oluşturucu şeylerinden sıkılıyordum.
okysabeni

78

UITableViewController, iOS 7'den sonra automaticAdjustsScrollViewInsets özelliğine sahiptir . Tablo görünümünde zaten contentOffset olabilir, genellikle (0, -64).

Dolayısıyla, programlama yoluyla yenilemeye başladıktan sonra renewControl'ü göstermenin doğru yolu, refreshControl'ün yüksekliğini mevcut contentOffset'e eklemektir.

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

merhaba, çok teşekkür ederim, hata ayıklarken karşılaştığım sihirli noktayı da merak ediyorum (0, -64).
inix

2
@inix 20 durum çubuğu yüksekliği için + 44 gezinme çubuğu yüksekliği için
Igotit

1
Bunun yerine -64kullanmak daha iyidir-self.topLayoutGuide.length
Diogo T

33

İşte yukarıda açıklanan stratejileri kullanan bir Swift uzantısı.

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 için çalışıyor.
Juan Boero

10
sendActionsForControlEvents(UIControlEvents.ValueChanged)Bu fonksiyonun sonuna koymanızı tavsiye ederim , aksi takdirde gerçek yenileme mantığı çalışmayacaktır.
Colin Basnett

Bunu yapmanın açık ara en zarif yolu budur. Daha iyi işlevsellik için Colin Basnett'in yorumu dahil. bir kez tanımlanarak tüm proje genelinde kullanılabilir!
JoeGalind

1
@ColinBasnett: Bu kodu eklemek (artık sendActions (için: UIControlEvents.valueChanged)) sonsuz bir döngü ile sonuçlanır ...
Kendall Helmstetter Gelner

15

Diğer cevapların hiçbiri benim için işe yaramadı. Döndürücünün göstermesine ve dönmesine neden olurlar, ancak yenileme eyleminin kendisi asla gerçekleşmez. Bu çalışıyor:

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

Bu örnekte bir tablo görünümü kullanıldığına dikkat edin, ancak aynı zamanda bir koleksiyon görünümü de olabilir.


Bu benim sorunumu çözdü. Ama şimdi kullanıyorum SVPullToRefresh, programlı olarak nasıl indirebilirim?
2014

1
@Gank SVPullToRefresh'i hiç kullanmadım. Dokümanlarını okumayı denediniz mi? Dokümanlara göre programlı olarak aşağı çekilebileceği oldukça açık görünüyor: "Yenilemeyi programlı olarak tetiklemek istiyorsanız (örneğin, viewDidAppear :), bunu şu şekilde yapabilirsiniz: [tableView triggerPullToRefresh];" Bkz: github.com/samvermette/ SVPullToRefresh
Kyle Robson

1
Mükemmel! Yine de animasyonla benim için garip davranıyordu, bu yüzden onu basitçescrollView.contentOffset = CGPoint(x: 0, y: scrollView.contentOffset.y - frame.height)
user1366265

13

Daha önce bahsedilen yaklaşım:

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

iplikçiyi görünür kılar. Ama canlanmıyordu. Değiştirdiğim tek şey, bu iki yöntemin sırasıydı ve her şey işe yaradı:

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

11

Swift 4 / 4.1 için

Mevcut cevapların bir karışımı benim için işi yapıyor:

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

Bu yardımcı olur umarım!


8

İşte spinner'ı gösteren ve onu canlandıran Swift 3 ve sonraki uzantı.

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
Yukarıdaki tüm cevaplar arasında iOS 11.1 / xcode 9.1 üzerinde çalışabildiğim tek cevap
buydu

1
asyncAfterDönen animasyonun çalışmasını sağlayan şey aslında budur (iOS 12.3 / Xcode 10.2)
francybiga

7

Ayrıca bu soruya bakın

BeginRefreshing ve contentOffset çağrılırken UIRefreshControl dikenli göstermiyor

Bana bir hata gibi görünüyor, çünkü yalnızca tableView'ün contentOffset özelliği 0 olduğunda ortaya çıkıyor

Bunu aşağıdaki kodla düzelttim (UITableViewController yöntemi):

- (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
Bu doğru değil, iki farklı UIViewControllers'ım var, her ikisi de viewDidLoad üzerine contentOffset 0 değerine sahip ve bunlardan biri [self.refreshControl beginRefreshing] 'i çağırdığında yenilemeControl'ü doğru bir şekilde aşağı çekiyor ve diğeri değil: /
simonthumper

Dokümantasyon, kontrolün gösterilmesi hakkında hiçbir şey söylemez beginRefreshing, sadece durumu değişir. Gördüğüm kadarıyla, yenileme eyleminin iki kez başlatılmasını engellemek, böylece programla yenileme adı verilen bir yenileme hala çalışıyor olabilir, kullanıcı tarafından başlatılan bir eylem başka bir eylem başlatmaz.
Koen.

SetContentOffset: animated yöntemiyle ilgili sorunları not ettim, bu nedenle bu çözüm benim için çalıştı.
Marc Etcheverry

4

Benim için mükemmel çalışıyor:

Swift 3:

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

4

Swift 5 için benim için eksik olan tek şey aramaktı refreshControl.sendActions(.valueChanged). Daha temiz hale getirmek için bir uzantı yaptım.

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 çözümüne ek olarak.

Bu sorun için güzel bir çözüm buldum. Bu UIRefreshControlüzerine yazma yöntemine uzantı oluşturabilirsiniz :

// 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'da yenileme kontrolü için Identity Inspector'da özel sınıf ayarlayarak yeni sınıfı kullanabilirsiniz .


3

Fort Swift 2.2+

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

Bunu sadece bir güncelleme olduğu için kabul edilen cevapla birleştirdim.
Tudor

3

Swift 3.1 için Rxswift kullanıyorsanız, aşağıdakileri kullanabilirsiniz:

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

Bu, hızlı 3.1, iOS 10 için çalışır.


1
Onun sendActionstetiğe rxilgili bu cevabı yapar RxSwiftörtmek herkes ilk bakış at merak ediyor
carbonr

TableView yerine tableViewController'dan yenilemeControl örneğini kullanmak zorunda kaldım. Ayrıca setContentOffset, iOS10'u hedeflemem için çalışmadı. Ancak bu işe yarıyor: self.tableView.setContentOffset(CGPoint(x:0, y:self.tableView.contentOffset.y - (refreshControl.frame.size.height)), animated: true)
nmdias

1

Swift 5'te test edildi

bunu içinde kullan 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

Kullanıcıya "veri güncellenir" görsel işaretini göstermek için aynı tekniği kullanıyorum. Bir sonuç kullanıcı arka plandan uygulama getirir ve beslemeler / listeler, kullanıcıların kendilerini yenilemek için tabloları çekmesi gibi UI ile güncellenir. Benim versiyonum 3 şey içeriyor

1) "Uyan" gönderen

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

2) UIViewController'da Gözlemci

- (void)viewDidLoad {
    [super viewDidLoad];

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

3) Protokol

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

sonuç

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.