UIRefreshControl ที่ไม่มี UITableViewController


308

แค่อยากรู้อยากเห็นเพราะมันดูเหมือนจะเป็นไปไม่ได้ในทันที แต่มีวิธีลับๆล่อๆเพื่อใช้UIRefreshControlคลาสiOS 6 ใหม่โดยไม่ต้องใช้UITableViewControllerคลาสย่อยหรือไม่?

ฉันมักจะใช้UIViewControllerกับย่อยUITableViewและสอดคล้องกับUITableViewDataSourceและUITableViewDelegateมากกว่าการใช้UITableViewControllerทันที


6
@DaveDeLong: ไม่เดฟเขาควรทำอย่างไร? ต่อมาในหน้านี้คุณบอกว่าวิธีการแก้ปัญหาของเขาไม่ได้รับการสนับสนุนดังนั้นทางออกที่ถูกต้องคืออะไร?
แมตต์

3
@matt เขาควรใช้ a UITableViewControllerและยื่นข้อผิดพลาดที่ร้องขอ API ให้ใช้ a UIRefreshControlด้วย a UITableViewโดยตรง
เดฟ DeLong

31
UITableViewController มี (และยังคงมีอยู่) มีข้อบกพร่องที่คลุมเครือและเฉพาะเจาะจงมากเกินไปและปัญหาเกี่ยวกับลำดับชั้นการดูที่ไม่สำคัญ ... ซึ่งทั้งหมดหายไปอย่างน่าอัศจรรย์เมื่อคุณเปลี่ยนไปใช้ VC มาตรฐานที่มีมุมมองย่อย TableView "Use UITVC" เป็นการเริ่มต้นที่ไม่ดีสำหรับโซลูชันใด ๆ IMHO
อดัม

@ อดัมทำข้อบกพร่องเหล่านี้เมื่อใช้ 'UITableViewController' เป็นตัวควบคุมมุมมองเด็ก (ซึ่งให้สิทธิ์ในการเข้าถึงการปรับแต่งมุมมองโดยไม่ต้องทำการแมปในลำดับชั้นของ tableViewControllers)? ฉันไม่เคยพบปัญหาเมื่อใช้ด้วยวิธีนี้
memmons

คำตอบ:


388

เกี่ยวกับลางสังหรณ์และขึ้นอยู่กับแรงบันดาลใจ DrummerB ของฉันพยายามเพียงแค่เพิ่มUIRefreshControlเช่นเป็น subview 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
FYI นี่เป็นพฤติกรรมที่ไม่ได้รับการสนับสนุนและอาจมีการเปลี่ยนแปลงในอนาคต การรับประกันเพียงอย่างเดียวที่แอปเปิ้ลทำคือการใช้ตาม API ที่ให้มา (ในกรณีนี้-[UITableViewController setRefreshControl:]) จะยังคงทำงานต่อไป
Dave DeLong

18
ด้วยการใช้งานนี้ดูเหมือนว่าถูกต้องก่อนที่จะก่อให้เกิดhandleRefresh:การเปลี่ยนแปลงที่ไม่คาดคิดในมูลค่าของมุมมองเลื่อนcontentInsetsสำหรับเสี้ยววินาที ใครมีประสบการณ์นี้หรือมีการแก้ไขหรือไม่ (ใช่ฉันรู้ว่านี่ไม่ได้รับการสนับสนุนในตอนแรก!)
ทิม

6
คุณควรใช้มุมมองคอนเทนเนอร์เพื่อทำสิ่งนี้ เพียงแค่ตั้งคลาสของตัวควบคุมมุมมองเป็นคลาสย่อยที่กำหนดเองUITableViewControllerและคุณจะ "ทำถูกต้อง"
Eugene

3
ใน iOS7 (ไม่ทราบสำหรับ iOS6) รุ่นที่แก้ไขจะทำงานได้ดียกเว้นเมื่อคุณปิดและเปิดแอปอีกครั้ง ภาพเคลื่อนไหวจะไม่เล่นจนกว่าจะถึงเวลาที่คุณจะรีเฟรช ครั้งแรกที่รีเฟรชหลังจากเปิดแอปอีกครั้งมันเป็นเพียงวงกลมเต็มแทนที่จะเป็นวงกลมที่เพิ่มขึ้นตามระยะเวลาที่คุณดึงลงมา มีวิธีแก้ไขไหม?
david2391

30
เพื่อหลีกเลี่ยงการมี "slutter" ดังกล่าวข้างต้นในขณะที่ยังผ่าน UITableViewController [_tableView insertSubview:_refreshControl atIndex:0];เพียงแค่เพิ่มการควบคุมการฟื้นฟูภายใต้มุมมองตารางเช่นดังนั้น: ทดสอบกับทั้ง iOS 7 และ 8;)
ptitvinou

95

เพื่อขจัดพูดติดอ่างที่เกิดจากคำตอบที่ได้รับการยอมรับคุณสามารถกำหนดของคุณไปUITableViewUITableViewController

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

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

_theTableView = _tableViewController.tableView;

แก้ไข:

วิธีเพิ่ม a 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: existingTableView.style - จากนั้นทำ newTableViewController.tableView = existingTableView และกำหนด refreshControl เดือดลงไปสามบรรทัด
Trenskow

อย่าลืมโทร[_tablewViewController didMoveToParentViewController:self];หลังจากเพิ่มมุมมองย่อย
qix

4
self.tableView = _tableViewController.tableView;ควรเป็น_tableViewController.tableView = self.tableView
Ali

@Lusus ทำไมจึงเป็นสิ่งที่จำเป็น? ฉันเพิ่ม _tableViewController.view เป็น subView ในเมธอด viewDidLoad ของ viewController ที่กำหนดเองของฉันและดูเหมือนว่าจะทำเคล็ดลับ ฉันไม่จำเป็นต้องตั้งค่า _tableViewController.tableView
taber

เหตุผลที่ฉันไม่สามารถใช้ UITableViewController และใช้ UIViewController w / a ดูตารางภายใน stackoverflow.com/questions/18900428/…
lostintranslation

21

สิ่งที่คุณควรลองคือใช้มุมมองคอนเทนเนอร์ภายใน ViewController ที่คุณใช้ คุณสามารถกำหนดคลาสย่อย clean UITableViewController ด้วย tableview เฉพาะและวางไว้ใน ViewController


2
แม่นยำดูเหมือนว่าวิธีที่สะอาดที่สุดคือการเพิ่มตัวควบคุมมุมมองลูกของประเภท UITableViewController
Zdenek

จากนั้นคุณสามารถคว้า UITableViewController self.childViewControllers.firstObjectโดยใช้
cbh2000

^ คุณอาจต้องการพิจารณาใช้prepareForSegueเพื่อรับการอ้างอิงถึงวัตถุ UITableViewController ในมุมมองคอนเทนเนอร์เนื่องจากสิ่งนี้จะถูกเรียกใช้ทันทีเป็นเหตุการณ์ต่อเนื่องในการสร้างอินสแตนซ์จากสตอรีบอร์ด
crarho

18

UIRefreshControl เป็นคลาสย่อยของ UIView ดังนั้นคุณสามารถใช้มันด้วยตัวเอง ฉันไม่แน่ใจว่ามันจะแสดงผลอย่างไร การเรนเดอร์อาจขึ้นอยู่กับเฟรม แต่ก็สามารถพึ่งพา UIScrollView หรือ UITableViewController

ไม่ว่าจะด้วยวิธีใดมันจะเป็นการแฮ็คมากกว่าโซลูชันที่สง่างาม ฉันขอแนะนำให้คุณดูเป็นหนึ่งในโคลนของบุคคลที่สามที่มีอยู่หรือเขียนของคุณเอง

ODRefreshControl

ป้อนคำอธิบายรูปภาพที่นี่

SlimeRefresh

ป้อนคำอธิบายรูปภาพที่นี่


1
ขอบคุณสำหรับการตอบกลับของคุณ แต่ไม่ใช่สิ่งที่ฉันกำลังมองหา อย่างไรก็ตามประโยคแรกของการโพสต์ของคุณทำให้ฉันเป็นแรงบันดาลใจให้ลองทำสิ่งที่เป็นวิธีแก้ปัญหา!
Keller

7
ODRefreshControl ยอดเยี่ยม - ขอบคุณสำหรับเคล็ดลับ! ง่ายมากและทำงานได้ดี และแน่นอนว่ามีความยืดหยุ่นมากกว่า Apple ฉันไม่เคยเข้าใจว่าทำไมทุกคนจะใช้ UITableViewController ซึ่งเป็น IMO ที่แย่ที่สุดใน API ทั้งหมด มันไม่ทำอะไรเลย (แต่ถัดไป) แต่ทำให้มุมมองของคุณไม่ยืดหยุ่น
n13

มันขึ้นอยู่กับโครงการของคุณว่าคุณใช้อะไรในนั้น .. การใช้ uitableview ในกรณีของฉันเพิ่มความซับซ้อนของโครงการและตอนนี้ฉันเปลี่ยนไปใช้ uitableviewcontroller ซึ่งการแบ่งรหัสของฉันออกเป็นสองส่วนฉันมีความสุขฉันมี 2 ไฟล์ขนาดเล็ก นำมาใช้เพื่อแก้ปัญหาแทนหนึ่งไฟล์ขนาดใหญ่ ..
kush

@ n13 สวัสดีเหตุผลที่ดีในการใช้ UITableViewController คือความสามารถในการออกแบบด้วยสแตติกเซลล์ น่าเสียดายที่ไม่สามารถใช้งานได้ใน tableView ที่อยู่ใน UIViewController
Jean Le Moignan

@JeanLeMoignan เครื่องมือและไลบรารี iOS กำลังเปลี่ยนแปลงอย่างรวดเร็วดังนั้นความคิดเห็นอายุ 3 ปีของฉันจึงไม่สามารถใช้งานได้อีกต่อไป ฉันได้เปลี่ยนไปใช้การสร้างอินเทอร์เฟซผู้ใช้ทั้งหมดในรหัสด้วย SnapKit และโดยสุจริตมันมีประสิทธิภาพมากกว่าการคลิก 10,000 ครั้งในเครื่องมือสร้างอินเตอร์เฟส การเปลี่ยน UITableViewController เป็น UIViewController นั้นใช้งานง่ายและใช้เวลาเพียงชั่ววินาทีในขณะที่ IB ก็เป็นความเจ็บปวดที่ยิ่งใหญ่
n13

7

ลองล่าช้าในการเรียกร้องให้ refreshControl -endRefreshวิธีโดยเสี้ยววินาทีหลังจาก tableView โหลดเนื้อหาของมันโดยใช้ NSObject ของ-performSelector:withObject:afterDelay:หรือ dispatch_afterGCD

ฉันสร้างหมวดหมู่บน 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นี่คือวิธีแก้ปัญหาสำหรับการใช้งานที่ได้รับการสนับสนุนอยู่แล้วของ
boliva

คุณสามารถเรียกวิธีที่มีความล่าช้าได้ง่ายขึ้นด้วย [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 ใหม่ใน Storyboard และฝังไว้ใน 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 self.view) และไม่ใช่แค่มุมมองปกติเท่านั้นมันสร้างลำดับชั้นควบคุม / มุมมองที่ไม่สอดคล้องกันและทำให้เกิดความผิดพลาด

โดยทั่วไปสร้างคลาสย่อยของคุณเองของ UITableViewController และแทนที่ loadView เพื่อกำหนดมุมมองที่แตกต่างกันของ self.view และแทนที่คุณสมบัติ tableView เพื่อส่งกลับ 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 ดูคอนโทรลเลอร์โดยตรง subclass ของ UITableViewController แทนการมีลูก UITableViewController

สิ่งที่ต้องระวัง:

เนื่องจากเรากำลังเอาชนะคุณสมบัติ tableView โดยไม่เรียก super อาจมีบางสิ่งที่ต้องระวังและควรจัดการในกรณีที่จำเป็น ตัวอย่างเช่นการตั้งค่า tableview ในตัวอย่างด้านบนของฉันจะไม่เพิ่ม tableview เป็น self.view และไม่ได้กำหนดเฟรมที่คุณอาจต้องการทำ นอกจากนี้ในการใช้งานนี้ไม่มี tableView เริ่มต้นที่กำหนดให้กับคุณเมื่อชั้นเรียนนั้นเป็นอินสแตนซ์ซึ่งเป็นสิ่งที่คุณอาจพิจารณาเพิ่ม ฉันไม่ได้รวมไว้ที่นี่เพราะเป็นกรณี ๆ ไปและวิธีการแก้ปัญหานี้เหมาะสมกับโซลูชันของเคลเลอร์


1
คำถามที่ระบุ "โดยไม่ใช้คลาสย่อย UITableViewController" และคำตอบนี้มีไว้สำหรับคลาสย่อย UITableViewController
Brian

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

คำแนะนำแรกของ Keller ทำให้เกิดข้อผิดพลาดแปลก ๆ ใน 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เป็นสถานที่ให้บริการสำหรับการไม่ได้UITableViewController UIViewController
Rickster

ไม่ทำงานการอ้างอิง hControl ไม่ได้ถูกกำหนดไว้สำหรับ uiviewcontroller
OMGPOP
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.