Comment faire pour que le déroulement d'une table de ScrollView à L'intérieur de ScrollView se comporte naturellement

j'ai besoin de faire cette application qui a une configuration bizarre.

comme indiqué dans l'image suivante, la vue principale est UIScrollView. Ensuite, à l'intérieur, il devrait y avoir un UIPageView, et chaque page de la PageView devrait avoir un UITableView.

figure

j'ai fait tout ça jusqu'à présent. Mais mon problème est que je veux le scrolling à se comporternaturellement.

La prochaine est ce que je veux dire naturellement. Actuellement, Quand je fais défiler sur l'un des Viewutables, il défile la tableview (pas la scrollview). Mais je veux qu'il fasse défiler le ScrollView à moins que le scrollview ne puisse faire défiler parce qu'il est arrivé à son sommet ou à son fond (dans ce cas, je voudrais qu'il fasse défiler le tableview).

par exemple, disons que mon scrollview est actuellement en haut. Puis je mets mon doigt sur la vue de la table (de la page courante étant affichée) et commence à faire défiler bas. Je dans ce cas, je veux que le scrollview fasse défiler (non le tableview). Si je continue à faire défiler mon scrollview et qu'il atteint le bas, si je retire mon doigt de l'affichage et le remet sur le tebleview et le fait défiler vers le bas à nouveau, je veux que mon tableview fasse défiler vers le bas maintenant parce que le scrollview a atteint son bas et qu'il n'est pas capable de continuer à défiler.

avez-vous une idée de comment implémenter ce défilement?

je suis vraiment perdu avec ça. Toute aide sera grandement l'apprécier :(

Merci!

45
demandé sur Luca Angeletti 2015-10-23 02:23:20

8 réponses

la solution pour gérer simultanément la vue scroll et la vue table tourne autour du UIScrollViewDelegate. Par conséquent, votre contrôleur de vue doit se conformer à ce protocole:

class ViewController: UIViewController, UIScrollViewDelegate {

je représente la vue scroll et la vue de table comme points de vente:

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!

nous aurons aussi besoin de suivre la hauteur du contenu de la vue par défilement ainsi que la hauteur de l'écran. Vous verrez pourquoi plus tard.

let screenHeight = UIScreen.mainScreen().bounds.height
let scrollViewContentHeight = 1200 as CGFloat

Un peu de configuration est nécessaire dans viewDidLoad::

override func viewDidLoad() {
    super.viewDidLoad()

    scrollView.contentSize = CGSizeMake(scrollViewContentWidth, scrollViewContentHeight)
    scrollView.delegate = self
    tableView.delegate = self 
    scrollView.bounces = false
    tableView.bounces = false
    tableView.scrollEnabled = false
}

où j'ai désactivé rebondir pour garder les choses simples. les paramètres clés sont les délégués pour la vue par défilement et la vue de table et avoir la vue de table défiler étant désactivé au début.

ceux-ci sont nécessaires pour que le scrollViewDidScroll: la méthode delegate permet d'atteindre le bas de la vue scroll et le haut de la vue table. Voici la méthode:

func scrollViewDidScroll(scrollView: UIScrollView) {
    let yOffset = scrollView.contentOffset.y

    if scrollView == self.scrollView {
        if yOffset >= scrollViewContentHeight - screenHeight {
            scrollView.scrollEnabled = false
            tableView.scrollEnabled = true
        }
    }

    if scrollView == self.tableView {
        if yOffset <= 0 {
            self.scrollView.scrollEnabled = true
            self.tableView.scrollEnabled = false
        }
    }
}

ce que la méthode du délégué fait est détection lorsque la vue scroll a atteint son fond. Lorsque cela s'est produit, la vue de la table peut être scrollée. Il détecte également lorsque la vue table atteint le haut où la vue scroll est réactivée.

j'ai créé un GIF pour démontrer les résultats:

enter image description here

42
répondu Daniel Zhang 2015-10-24 04:39:16

modifié la réponse de Daniel pour la rendre plus efficace et sans bogue.

@IBOutlet weak var scrollView: UIScrollView!
@IBOutlet weak var tableView: UITableView!
@IBOutlet weak var tableHeight: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    //Set table height to cover entire view
    //if navigation bar is not translucent, reduce navigation bar height from view height
    tableHeight.constant = self.view.frame.height-64
    self.tableView.isScrollEnabled = false
    //no need to write following if checked in storyboard
    self.scrollView.bounces = false
    self.tableView.bounces = true
}

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

func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    let label = UILabel(frame: CGRect(x: 0, y: 0, width: tableView.frame.width, height: 30))
    label.text = "Section 1"
    label.textAlignment = .center
    label.backgroundColor = .yellow
    return label
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
    cell.textLabel?.text = "Row: \(indexPath.row+1)"
    return cell
}

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    if scrollView == self.scrollView {
        tableView.isScrollEnabled = (self.scrollView.contentOffset.y >= 200)
    }

    if scrollView == self.tableView {
        self.tableView.isScrollEnabled = (tableView.contentOffset.y > 0)
    }
}

Le projet complet peut être consulté ici: https://gitlab.com/vineetks/TableScroll.git

12
répondu Vineet Kumar Singh 2017-09-09 18:32:17

Après de nombreux essais et erreurs, c'est ce qui fonctionne le mieux pour moi. La solution doit résoudre deux problèmes: 1) Déterminer la propriété de défilement à utiliser; tableView ou scrollView? 2) Assurez-vous que le tableView ne donne pas autorité au scrollView tant qu'il n'a pas atteint le haut de sa table/contenu.

pour voir si le scrollview doit être utilisé pour défiler contre le tableview, j'ai vérifié si L'uivi juste au-dessus de mon tableview était dans le cadre. Si le UIView est dans le cadre, il est sûr de dire que le scrollView devrait avoir l'autorité de faire défiler. Si UIView n'est pas dans frame, cela signifie que tableView occupe toute la fenêtre, et devrait donc avoir l'autorisation de faire défiler.

func scrollViewDidScroll(_ scrollView: UIScrollView) {

    if scrollView.bounds.intersects(UIView.frame) == true {
     //the UIView is within frame, use the UIScrollView's scrolling.   

        if tableView.contentOffset.y == 0 {
            //tableViews content is at the top of the tableView.

        tableView.isUserInteractionEnabled = false
       tableView.resignFirstResponder()
        print("using scrollView scroll")

        } else {

            //UIView is in frame, but the tableView still has more content to scroll before resigning its scrolling over to ScrollView.

            tableView.isUserInteractionEnabled = true
            scrollView.resignFirstResponder()
            print("using tableView scroll")
        }

    } else {

        //UIView is not in frame. Use tableViews scroll.

        tableView.isUserInteractionEnabled = true
       scrollView.resignFirstResponder()
        print("using tableView scroll")

    }

}

espérons que cela aide quelqu'un!

4
répondu Felecia Genet 2016-12-10 19:45:56

je pense qu'il y a deux options.

puisque vous connaissez la taille de la vue de défilement et de la vue principale, vous n'êtes pas en mesure de dire si la vue de défilement a atteint le bas ou non.

if (scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)) {
    // reach bottom
}

Donc quand il a frappé, vous avez défini

[contentScrollView setScrollEnabled:NO];

et l'autre manière autour de votre tableView.

l'autre chose, qui est plus précise je pense, est d'ajouter le geste à vos vues.

UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToTapGesture:)];

// Specify that the gesture must be a single tap
tapRecognizer.numberOfTapsRequired = 1;

// Add the tap gesture recognizer to the view
[self.view addGestureRecognizer:tapRecognizer];

// Do any additional setup after loading the view, typically from a nib

ainsi, lorsque vous ajoutez un geste, vous pouvez simplement contrôler vue active en changeant setScrollEnabled dans le respondToTapGesture.

1
répondu Berke Atmaca 2017-06-29 08:12:23

j'ai trouvé une super bibliothèque MXParallaxHeader

Dans le Storyboard juste set UIScrollView classe MXScrollView alors la magie se produit.

j'ai utilisé cette classe pour gérer mon UIScrollView quand je intégrer un UIPageViewController vue du conteneur. vous pouvez même insérer une parallaxe vue d'en-tête pour plus de détails.

aussi, cette bibliothèque fournit Cocoapods et Carthage

j'ai joint une image ci-dessous qui représentent UIViewHiérarchie. MXScrollView Hiérarchie

1
répondu Farshad Mousalou 2018-04-26 01:31:28

j'ai essayé la solution marqué comme réponse correcte, mais il ne fonctionnait pas correctement. L'utilisateur doit cliquer deux fois sur la vue de la table pour faire défiler et après cela je n'ai pas été en mesure de faire défiler l'écran entier à nouveau. Donc j'ai juste appliqué le code suivant dans viewDidLoad ():

tableView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(tableViewSwiped)))
scrollView.addGestureRecognizer(UISwipeGestureRecognizer(target: self, action: #selector(scrollViewSwiped)))

Et le code ci-dessous est la mise en œuvre des actions:

func tableViewSwiped(){
    scrollView.isScrollEnabled = false
    tableView.isScrollEnabled = true
}

func scrollViewSwiped(){
    scrollView.isScrollEnabled = true
    tableView.isScrollEnabled = false
}
0
répondu Vinícius Rocha 2017-05-08 11:36:29

Aucune des réponses ici a fonctionné parfaitement pour moi. Chacun d'eux avait son propre problème nuancé (besoin de faire un balayage répété quand un scrollview touche le fond, ou l'indicateur de rouleau ne regarde pas correctement, etc), donc j'ai pensé que je jetterais dans une autre réponse.

Ole Begemann a un bon rapport sur le fait de faire cela exactement https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/

bien qu'il s'agisse d'un ancien poste, les concepts s'appliquent toujours au API. De plus, il y a une mise en oeuvre de son approche (compatible avec le Xcode 9)https://github.com/eyeem/OLEContainerScrollView

0
répondu Andy Obusek 2018-04-20 13:59:40
CGFloat tableHeight = 0.0f;

YourArray =[response valueForKey:@"result"];

tableHeight = 0.0f;
for (int i = 0; i < [YourArray count]; i ++) {
    tableHeight += [self tableView:self.aTableviewDoc heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}

self.aTableviewDoc.frame = CGRectMake(self.aTableviewDoc.frame.origin.x, self.aTableviewDoc.frame.origin.y, self.aTableviewDoc.frame.size.width, tableHeight);
-1
répondu Bhadresh Baraiya 2016-08-22 13:34:16