UIRefreshControl không có UITableViewControll


308

Chỉ tò mò, vì điều đó dường như không thể ngay lập tức, nhưng có một cách lén lút để tận dụng lớp iOS 6 mới UIRefreshControlmà không cần sử dụng một UITableViewControllerlớp con?

Tôi thường sử dụng một UIViewControllervới một UITableViewsubview và tuân thủ UITableViewDataSourceUITableViewDelegatehơn là sử dụng UITableViewControllerhoàn toàn.


6
@DaveDeLong: Không, Dave, anh ấy nên làm gì? Sau này trên trang này bạn nói giải pháp của anh ấy không được hỗ trợ, vậy giải pháp nào là đúng?
matt

3
@matt anh ta nên sử dụng UITableViewControllervà gửi một API yêu cầu lỗi để sử dụng trực tiếp UIRefreshControlvới một UITableView.
Dave DeLong

31
UITableViewControll đã có (và tiếp tục có) quá nhiều lỗi và vấn đề khó hiểu với các hệ thống phân cấp chế độ xem không tầm thường ... tất cả đều biến mất một cách kỳ diệu khi bạn chuyển sang sử dụng một VC tiêu chuẩn với chế độ xem TableView. "Sử dụng UITVC" là một khởi đầu kém cho bất kỳ giải pháp nào, IMHO.
Adam

@Adam Các lỗi này có xuất hiện khi sử dụng 'UITableViewContaptor' làm trình điều khiển chế độ xem con hay không (do đó cung cấp quyền truy cập để xem tùy chỉnh mà không cần tìm kiếm trong hệ thống phân cấp của bảngViewControllers)? Tôi chưa bao giờ gặp phải vấn đề khi sử dụng nó theo cách này.
memmons

Câu trả lời:


388

Theo linh cảm, và dựa trên cảm hứng của DrummerB, tôi đã cố gắng chỉ đơn giản là thêm một UIRefreshControlví dụ như một cuộc phỏng vấn vào tôi UITableView. Và nó kỳ diệu chỉ hoạt động!

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

Điều này thêm một UIRefreshControlchế độ xem bảng của bạn ở trên và hoạt động như mong đợi mà không phải sử dụng UITableViewController:)


EDIT: Điều này ở trên vẫn hoạt động nhưng như một số ít đã chỉ ra, có một chút "nói lắp" khi thêm UIRefreshControl theo cách này. Một giải pháp cho vấn đề đó là khởi tạo một UITableViewControll và sau đó đặt UIRefreshControl và UITableView của bạn thành đó, tức là:

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
FYI, đây là hành vi không được hỗ trợ và có thể thay đổi trong tương lai. Bảo đảm duy nhất mà Apple đưa ra là việc sử dụng nó theo api được cung cấp (trong trường hợp này -[UITableViewController setRefreshControl:]) sẽ tiếp tục hoạt động.
Dave DeLong

18
Với cách triển khai này, dường như ngay trước khi kích hoạt, handleRefresh:có một sự thay đổi bất ngờ về giá trị của chế độ xem cuộn contentInsetstrong một giây. Bất cứ ai khác trải nghiệm điều này hoặc có một sửa chữa cho nó? (vâng, tôi biết điều này không được hỗ trợ ngay từ đầu!)
Tim

6
Bạn thực sự chỉ nên sử dụng Chế độ xem Container cho việc này. Chỉ cần đặt lớp của trình điều khiển khung nhìn của nó thành lớp con tùy chỉnh của UITableViewControllerbạn và bạn sẽ "làm đúng".
Eugene

3
Trong iOS7 (không xác định cho iOS6), phiên bản chỉnh sửa hoạt động tốt trừ khi bạn đóng và mở lại ứng dụng. Hoạt hình không phát cho đến lần thứ 2 bạn làm mới. Lần đầu tiên làm mới sau khi mở lại ứng dụng, nó chỉ là một vòng tròn đầy đủ thay vì vòng tròn tăng dần dựa trên khoảng cách bạn kéo nó xuống. Có sửa chữa không?
david2391

30
Để tránh có "sự lộn xộn" như đã đề cập ở trên trong khi vẫn bỏ qua UITableViewControll, chỉ cần thêm điều khiển làm mới bên dưới chế độ xem bảng như vậy : [_tableView insertSubview:_refreshControl atIndex:0];. Đã thử nghiệm với cả iOS 7 và 8;)
ptitvinou

95

Để loại bỏ sự nói lắp gây ra bởi câu trả lời được chấp nhận, bạn có thể gán UITableViewcho a 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;

BIÊN TẬP:

Một cách để thêm UIRefreshControlkhông UITableViewControllercó không nói lắp và giữ lại hình ảnh động đẹp sau khi làm mới dữ liệu trên bảng xem.

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

Sau này khi xử lý dữ liệu được làm mới ...

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

7
Bạn thậm chí không phải tạo một UITableView mới. Chỉ cần nói initWithStyle: currentTableView.style - và sau đó thực hiện newTableViewControll.tableView = currentTableView và gán refreshControl. Luộc này xuống ba dòng.
Trenskow

Đừng quên gọi [_tablewViewController didMoveToParentViewController:self];sau khi thêm subview.
qix

4
self.tableView = _tableViewController.tableView;nên là_tableViewController.tableView = self.tableView
Ali

@Linus tại sao lại cần thiết? Tôi đã thêm _tableViewControll.view dưới dạng subView trong phương thức viewDidLoad tùy chỉnh của viewControll và điều đó dường như thực hiện thủ thuật. Tôi thậm chí không cần phải đặt _tableViewControll.tableView
taber

Lý do mà tôi không thể sử dụng UITableViewControll và đang sử dụng UIViewControll với chế độ xem bảng bên trong. stackoverflow.com/questions/18900428/ trên
mấtintranslation

21

Những gì bạn sẽ thử là sử dụng chế độ xem container bên trong ViewContoder mà bạn đang sử dụng. bạn có thể định nghĩa lớp con UITableViewControll sạch với chế độ xem bảng chuyên dụng và đặt nó trong ViewContoder.


2
Chính xác, cách sạch nhất dường như là thêm trình điều khiển xem con của loại UITableViewControll.
Zdenek

Sau đó, bạn có thể lấy UITableViewControll bằng cách sử dụng self.childViewControllers.firstObject.
cbh2000

^ bạn cũng có thể muốn xem xét sử dụng prepareForSegueđể có được một tham chiếu đến đối tượng UITableViewControll trong chế độ xem vùng chứa vì điều này ngay lập tức được kích hoạt như một sự kiện riêng biệt khi khởi tạo từ bảng phân cảnh.
crarho

18

Chà, UIRefreshControl là một lớp con UIView, vì vậy bạn có thể tự mình sử dụng nó. Tôi không chắc chắn mặc dù nó làm thế nào. Việc kết xuất có thể đơn giản phụ thuộc vào khung, nhưng nó cũng có thể dựa vào UIScrollView hoặc UITableViewCont kiểm soát.

Dù bằng cách nào, nó sẽ trở thành một hack hơn là một giải pháp tao nhã. Tôi khuyên bạn nên xem xét một trong những bản sao của bên thứ 3 có sẵn hoặc tự viết.

ODRefreshControl

nhập mô tả hình ảnh ở đây

SlimeRefresh

nhập mô tả hình ảnh ở đây


1
Cảm ơn phản hồi của bạn, nhưng không chính xác những gì tôi đang tìm kiếm. Tuy nhiên, câu đầu tiên của bài viết của bạn đã làm, truyền cảm hứng cho tôi để thử những gì cuối cùng là giải pháp!
Keller

7
ODRefreshControl thật tuyệt vời - cảm ơn vì tiền boa! Rất đơn giản, và thực hiện công việc. Và tất nhiên linh hoạt hơn nhiều so với Apple. Tôi không bao giờ hiểu tại sao mọi người sẽ sử dụng UITableViewControll, IMO là lớp tồi tệ nhất trong toàn bộ API. Nó không (bên cạnh) không có gì, nhưng làm cho quan điểm của bạn không linh hoạt.
n13

Nó thực sự phụ thuộc vào dự án của bạn, bạn sử dụng cái gì trong đó được sử dụng để gỡ lỗi thay vì một tệp lớn ..
kush

@ n13 Xin chào, một lý do chính đáng để sử dụng UITableViewControll là có thể thiết kế với các ô tĩnh. Thật không may, không có sẵn trong một bảng Xem trong UIViewControll.
Jean Le Moignan

@JeanLeMoignan Các công cụ và thư viện iOS đang thay đổi nhanh chóng nên nhận xét 3 năm của tôi không còn hiệu lực. Tôi đã chuyển sang tạo tất cả các giao diện người dùng bằng mã với SnapKit và thực sự nó hiệu quả hơn nhiều so với việc nhấp 10 nghìn lần trong trình tạo giao diện. Ngoài ra, việc thay đổi UITableViewContaptor thành UIViewControll rất dễ dàng và chỉ mất một giây, trong khi ở IB thì đó là một nỗi đau lớn.
n13

7

Hãy thử trì hoãn cuộc gọi đến -endRefreshphương thức refreshControl trong một phần giây sau khi bảngView tải lại nội dung của nó, bằng cách sử dụng NSObject -performSelector:withObject:afterDelay:hoặc GCD dispatch_after.

Tôi đã tạo một danh mục trên UIRefreshControl cho việc này:

@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

Tôi đã thử nó và nó cũng hoạt động trên Collection Views. Tôi đã nhận thấy rằng độ trễ nhỏ đến 0,01 giây là đủ:

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

4
Trì hoãn và chờ đợi là phong cách thực sự xấu.
orkoden

2
@orkoden Tôi đồng ý. Đây là một cách giải quyết cho việc sử dụng đã không được hỗ trợ UIRefreshControl.
boliva

Bạn có thể gọi một phương thức với độ trễ dễ dàng hơn rất nhiều với. [self performSelector:@selector(endRefreshing) withObject:nil afterDelay:0.01]
orkoden

2
Hiệu ứng cuối cùng hoàn toàn giống nhau và không làm cho 'hack' trở nên thanh lịch hơn. Việc sử dụng -performSelector:withObject:afterDelay:hay GCD cho việc này tùy thuộc vào sở thích cá nhân.
boliva

7

IOS 10 Swift 3.0

nó đơn giản

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
    }

}

nhập mô tả hình ảnh ở đây

Nếu bạn muốn tìm hiểu về iOS 10 UIRefreshControl đọc tại đây .


3

Thêm điều khiển làm mới dưới dạng một khung nhìn phụ sẽ tạo một khoảng trống phía trên các tiêu đề của phần.

Thay vào đó, tôi đã nhúng một UITableViewControll vào UIViewControll của mình, sau đó thay đổi thuộc tính của tôi tableViewđể hướng tới một nhúng và viola! Thay đổi mã tối thiểu. :-)

Các bước:

  1. Tạo một UITableViewControll mới trong Storyboard và nhúng nó vào UIViewControll ban đầu của bạn
  2. Thay thế @IBOutlet weak var tableView: UITableView!bằng một từ UITableViewControll mới được nhúng, như hiển thị bên dưới

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

Đối với Swift 2.2.

Đầu tiên thực hiện UIRefreshControl ().

var refreshControl : UIRefreshControl!

Trong phương thức viewDidLoad () của bạn thêm:

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

Và làm cho chức năng làm mới

func refresh(refreshControl: UIRefreshControl) {

    // do something ...

    // reload tableView
    self.tableView.reloadData()

    // End refreshing
    refreshControl.endRefreshing()
}

0

Đây là một giải pháp khác một chút.

Tôi đã phải sử dụng nó vì một số vấn đề về phân cấp khung nhìn mà tôi gặp phải: Tôi đang tạo một số chức năng yêu cầu chuyển các khung nhìn xung quanh đến các vị trí khác nhau trong hệ thống phân cấp khung nhìn, bị hỏng khi sử dụng chế độ xem bảng của UITableViewContaptor b / c bảngView là chế độ xem gốc của UITableViewControll ( self.view) và không chỉ là một chế độ xem thông thường, nó đã tạo ra các cấu trúc điều khiển / chế độ xem không nhất quán và gây ra sự cố.

Về cơ bản tạo lớp con UITableViewControll của riêng bạn và ghi đè loadView để gán self.view một khung nhìn khác và ghi đè lên thuộc tính tableView để trả về một chế độ xem bảng riêng biệt.

ví dụ:

@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

Khi được kết hợp với giải pháp của Keller, điều này sẽ mạnh mẽ hơn theo nghĩa là Chế độ xem bảng bây giờ là chế độ xem thông thường, không phải là chế độ xem gốc của VC và mạnh mẽ hơn đối với việc thay đổi phân cấp chế độ xem. Ví dụ về việc sử dụng nó theo cách này:

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;

Có một cách sử dụng khác có thể cho việc này:

Vì việc phân lớp theo cách này tách biệt self.view khỏi self.tableView, giờ đây có thể sử dụng UITableViewContaptor này như một bộ điều khiển thông thường và thêm các cuộc phỏng vấn khác vào self.view mà không có sự khác biệt khi thêm các cuộc phỏng vấn vào UITableView xem các bộ điều khiển trực tiếp một lớp con của UITableViewControll thay vì có các con UITableViewControll.

Một số điều cần chú ý:

Vì chúng ta ghi đè lên thuộc tính tableView mà không gọi siêu, có thể có một số điều cần chú ý và nên xử lý khi cần thiết. Ví dụ: đặt chế độ xem bảng trong ví dụ trên của tôi sẽ không thêm chế độ xem bảng vào self.view và không đặt khung mà bạn có thể muốn làm. Ngoài ra, trong triển khai này, không có bảngView mặc định nào được cung cấp cho bạn khi lớp được khởi tạo, đây cũng là thứ bạn có thể xem xét thêm. Tôi không bao gồm nó ở đây bởi vì đó là tùy từng trường hợp và giải pháp này thực sự phù hợp với giải pháp của Keller.


1
Câu hỏi được chỉ định "không sử dụng lớp con UITableViewControll" và câu trả lời này dành cho lớp con UITableViewContaptor.
Brian

0

Thử cái này,

Các giải pháp trên đều ổn nhưng tableView.refreshControl chỉ khả dụng cho UITableViewControll, cho đến iOS 9.x và có trong UITableView từ iOS 10.x trở đi.

Viết bằng 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)

Giải pháp tốt nhất cho đến nay
Karthik KM

-1

Gợi ý đầu tiên của Keller gây ra một lỗi lạ trong iOS 7 khi phần bên trong của bảng được tăng lên sau khi trình điều khiển xem xuất hiện lại. Thay đổi câu trả lời thứ hai, sử dụng bộ điều khiển uitableview, đã sửa lỗi cho tôi.


-6

Hóa ra bạn có thể sử dụng những điều sau đây khi bạn sử dụng UIViewContoder với một khung nhìn phụ UITableView và tuân thủ UITableViewDataSource và UITableViewDelegate:

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

9
Không đồng ý. self.refreshControllà một tài sản cho a UITableViewController, không phải a UIViewController.
Rickster

KHÔNG LÀM VIỆC, refershControl không được xác định cho uiviewcontroll
OMGPOP
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.