it-swarm.com.de

Verschieben Sie eine Ansicht, wenn Sie in UITableView scrollen

Ich habe eine UIView mit einer UITableView darunter:

enter image description here

Ich möchte, dass die Ansicht über der Variablen UITableView nach oben (aus dem Weg) verschoben wird, wenn der Benutzer in der Tabelle zu scrollen beginnt, um mehr Platz für die Variable UITableView zu haben (und heruntergefahren wird, wenn Sie erneut nach unten scrollen).

Ich weiß, dass dies normalerweise mit einer Tabellenkopfansicht geschieht, aber mein Problem ist, dass sich meine Tabellenansicht auf einer Registerkarte befindet (tatsächlich handelt es sich um eine seitliche Bildlaufansicht, die mit TTSliddingPageviewcontroller implementiert ist). Ich habe zwar nur eine oberste UIView, aber drei UITableViews.

Ist es möglich, dies manuell durchzuführen? Mein erster Gedanke ist, alles in eine UIScrollView zu setzen, aber laut Apple-Dokumentation sollte niemals eine UITableView in eine UIScrollView eingefügt werden, da dies zu unvorhersehbarem Verhalten führt.

23
pajevic

Da UITableView eine Unterklasse von UIScrollView ist, kann der Delegat Ihrer Tabellensicht UIScrollViewDelegate-Methoden erhalten.

Im Delegat Ihrer Tabellenansicht:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    static CGFloat previousOffset;
    CGRect rect = self.view.frame;
    rect.Origin.y += previousOffset - scrollView.contentOffset.y;
    previousOffset = scrollView.contentOffset.y;
    self.view.frame = rect;
}
17
NobodyNada

Lösung für Swift (Funktioniert perfekt mit Bounce-Funktion für die Bildlaufansicht):

 var oldContentOffset = CGPointZero
 let topConstraintRange = (CGFloat(120)..<CGFloat(300))

 func scrollViewDidScroll(scrollView: UIScrollView) {

    let delta =  scrollView.contentOffset.y - oldContentOffset.y

    //we compress the top view
    if delta > 0 && topConstraint.constant > topConstraintRange.start && scrollView.contentOffset.y > 0 {
        topConstraint.constant -= delta
        scrollView.contentOffset.y -= delta
    }

    //we expand the top view
    if delta < 0 && topConstraint.constant < topConstraintRange.end && scrollView.contentOffset.y < 0{
        topConstraint.constant -= delta
        scrollView.contentOffset.y -= delta
    }

    oldContentOffset = scrollView.contentOffset
 }
23
George Muntean

Swift 3 & 4:

    var oldContentOffset = CGPoint.zero
    let topConstraintRange = (CGFloat(0)..<CGFloat(140))

    func scrollViewDidScroll(_ scrollView: UIScrollView) {

        let delta =  scrollView.contentOffset.y - oldContentOffset.y

        //we compress the top view
        if delta > 0 && yourConstraint.constant > topConstraintRange.lowerBound && scrollView.contentOffset.y > 0 {
            yourConstraint.constant -= delta
            scrollView.contentOffset.y -= delta
        }

        //we expand the top view
        if delta < 0 && yourConstraint.constant < topConstraintRange.upperBound && scrollView.contentOffset.y < 0{
            yourConstraint.constant -= delta
            scrollView.contentOffset.y -= delta
        }
        oldContentOffset = scrollView.contentOffset
    }
7
Dvole

Einfacher und schneller Ansatz 

- (void)scrollViewDidScroll:(UIScrollView *)scrollView 
{

    CGRect rect = self.view.frame;
    rect.Origin.y =  -scrollView.contentOffset.y;
    self.view.frame = rect;

}

Ich habe der letzten Lösung einige Einschränkungen hinzugefügt, um seltsame Verhaltensweisen beim schnellen Scrollen zu verhindern

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let delta =  scrollView.contentOffset.y - oldContentOffset.y

    //we compress the top view
    if delta > 0 && topConstraint.constant > topConstraintRange.lowerBound && scrollView.contentOffset.y > 0 {
        searchHeaderTopConstraint.constant = max(topConstraintRange.lowerBound, topConstraint.constant - delta)
        scrollView.contentOffset.y -= delta
    }

    //we expand the top view
    if delta < 0 && topConstraint.constant < topConstraintRange.upperBound && scrollView.contentOffset.y < 0 {
        topConstraint.constant = min(searchHeaderTopConstraint.constant - delta, topConstraintRange.upperBound)
        scrollView.contentOffset.y -= delta
    }
    oldContentOffset = scrollView.contentOffset
}
3
Exception

Jemand hat nach dem Code für meine Lösung gefragt, deshalb poste ich ihn hier als Antwort. Der Kredit für die Idee sollte noch an NobodyNada gehen.

In meiner UITableViewController implementiere ich diese Delegat-Methode:

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"TableViewScrolled" object:nil userInfo:scrollUserInfo];
}

scrollUserInfo ist eine NSDictionary, bei der ich meine UITableView mit der Benachrichtigung übergeben werde (ich mache dies in viewDidLoad, sodass ich es nur einmal tun muss):

scrollUserInfo = [NSDictionary dictionaryWithObject:self.tableView forKey:@"scrollView"];

Jetzt im View-Controller, der die Ansicht hat, die ich beim Scrollen vom Bildschirm entfernen möchte, mache ich dies in viewDidLoad:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleScroll:) name:@"TableViewScrolled" object:nil];

Und zum Schluss habe ich die Methode:

- (void)handleScroll:(NSNotification *)notification {
    UIScrollView *scrollView = [notification.userInfo valueForKey:@"scrollView"];
    CGFloat currentOffset = scrollView.contentOffset.y;
    CGFloat height = scrollView.frame.size.height;
    CGFloat distanceFromBottom = scrollView.contentSize.height - currentOffset;

    if (previousOffset < currentOffset && distanceFromBottom > height) {
        if (currentOffset > viewHeight)
            currentOffset = viewHeight;
        self.topVerticalConstraint.constant += previousOffset - currentOffset;
        previousOffset = currentOffset;
    }
    else {
        if (previousOffset > currentOffset) {
            if (currentOffset < 0)
                currentOffset = 0;
            self.topVerticalConstraint.constant += previousOffset - currentOffset;
            previousOffset = currentOffset;
        }
    }
}

previousOffset ist eine Instanzvariable CGFloat previousOffset;.topVerticalConstraint ist eine NSLayoutConstraint, die als IBOutlet eingestellt ist. Es wird vom oberen Rand der Ansicht bis zum oberen Teil des Übersichtsblatts geführt und der Anfangswert ist 0.

Es ist nicht perfekt. Wenn der Benutzer beispielsweise sehr aggressiv nach oben blättert, kann die Bewegung der Ansicht ein bisschen ruckartig werden. Das Problem ist schlimmer für große Ansichten. Wenn die Sicht klein genug ist, gibt es kein Problem.

3
pajevic

Um diese Animation zu erstellen, 

 enter image description here

lazy var redView: UIView = {

    let view = UIView(frame: CGRect(x: 0, y: 0, width: 
    self.view.frame.width, height: 100))
    view.backgroundColor = .red
    return view
}()

var pageMenu: CAPSPageMenu?

override func viewDidLoad() {
     super.viewDidLoad()
     self.view.addSubview(redView)

    let rect = CGRect(x: 0, y: self.redView.frame.maxY, width: self.view.bounds.size.width, height:(self.view.bounds.size.height - (self.redView.frame.maxY)))
    pageMenu?.view.frame = rect
    self.view.addSubview(pageMenu!.view)

  }

override func scrollViewDidScroll(_ scrollView: UIScrollView) {
  let offset = scrollView.contentOffset.y

    if(offset > 100){
        self.redView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 0)
    }else{
        self.redView.frame = CGRect(x: 0, y: 0, width: self.view.bounds.size.width, height: 100 - offset)
    }

    let rect = CGRect(x: 0, y: self.redView.frame.maxY, width: self.view.bounds.size.width, height:(self.view.bounds.size.height - (self.redView.frame.maxY)))
    pageMenu?.view.frame = rect
}

sie müssen pageMenu.view mit Ihrem collectionView/tableView ändern.

1
Yassine En

Ich kenne diesen Beitrag sehr alt. Ich habe die oben genannten Lösungen ausprobiert, aber keiner hat für mich selbst versucht, hoffentlich kann es Ihnen helfen. Dieses Szenario ist ziemlich üblich, da Apple vorschlug, TableViewController nicht in ScrollView zu verwenden, da der Compiler verwirrt ist, wer angesprochen werden muss, da er zwei Delegatenrückrufe erhält - einen von ScrollViewDelegate und einen von UITableViewDelegate. 

Stattdessen können Sie ScrollViewDelegate verwenden und UITableViewScrolling deaktivieren.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    CGFloat currentOffSetY = scrollView.contentOffset.y;
    CGFloat diffOffset = self.lastContentOffset - currentOffSetY;

    self.scrollView.contentSize = CGSizeMake(self.scrollView.contentSize.width, 400 + [self.tableView contentSize].height);

    if (self.lastContentOffset < scrollView.contentOffset.y) {
        tableView.frame = CGRectMake(tableView.frame.Origin.x, tableView.frame.Origin.y , tableView.frame.size.width,  tableView.size.height - diffOffset);
    }

    if (self.lastContentOffset > scrollView.contentOffset.y) {
        tableView.frame = CGRectMake(tableView.frame.Origin.x, tableViewframe.Origin.y, tableViewframe.size.width,  tableView.frame.size.height + diffOffset);
    }

    self.lastContentOffset = currentOffSetY;
}

LastContentOffset ist hier CGFloat als Eigenschaft definiert

Die Ansichts-Hierarchie sieht wie folgt aus: ViewController -> View enthält ScrollView (dessen Delegat-Methode oben definiert ist) -> Contain TableView.

Mit dem obigen Code erhöhen und verringern Sie manuell die Höhe der Tabellenansicht zusammen mit der Inhaltsgröße von ScrollView.

Denken Sie daran, das Scrollen von TableView zu deaktivieren.

0
Rishabh Sanghvi