programing

ScrollView 내부의 TableView 스크롤이 자연스럽게 동작하도록 하는 방법

bestprogram 2023. 7. 31. 21:35

ScrollView 내부의 TableView 스크롤이 자연스럽게 동작하도록 하는 방법

구성이 이상한 이 앱을 해야 합니다.

다음 이미지에 표시된 것처럼 기본 보기는 UIScrollView입니다.그런 다음 내부에는 UIP 페이지 뷰가 있어야 하고, 페이지 뷰의 각 페이지에는 UITable 뷰가 있어야 합니다.

figure

저는 지금까지 이 모든 것을 해왔습니다.하지만 제 문제는 스크롤이 자연스럽게 작동하기를 원한다는 것입니다.

다음은 제가 자연스럽게 말하는 것입니다.현재 UI 테이블 보기 중 하나에서 스크롤하면 스크롤 보기가 아닌 테이블 보기가 스크롤됩니다.그러나 스크롤 보기가 위쪽이나 아래쪽으로 이동하여 스크롤할 수 없는 경우를 제외하고 스크롤 보기를 스크롤합니다(이 경우 테이블 보기를 스크롤했으면 합니다).

예를 들어 스크롤 보기가 현재 맨 위로 스크롤되었다고 가정해 보겠습니다.그런 다음 (표시되는 현재 페이지의) 표 보기 위에 손가락을 놓고 아래로 스크롤을 시작합니다.이 경우 스크롤 보기를 스크롤합니다(테이블 보기 아님).스크롤 뷰를 계속 아래로 스크롤하면 아래로 이동합니다. 디스플레이에서 손가락을 떼고 다시 테이블 뷰 위에 놓고 다시 아래로 스크롤하면 테이블 뷰가 아래로 이동합니다. 스크롤 뷰가 아래로 이동하여 계속 스크롤할 수 없기 때문입니다.

여러분은 이 스크롤링을 구현하는 방법에 대해 알고 있나요?

저는 이것에 정말로 길을 잃었습니다.어떤 도움이라도 주시면 대단히 감사하겠습니다 :(

감사합니다!

스크롤 뷰와 테이블 뷰를 동시에 처리하는 솔루션은UIScrollViewDelegate따라서 뷰 컨트롤러가 해당 프로토콜을 준수하도록 합니다.

class ViewController: UIViewController, UIScrollViewDelegate {

스크롤 뷰와 테이블 뷰를 아웃렛으로 표시합니다.

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

스크롤 보기 콘텐츠의 높이와 화면 높이도 추적해야 합니다.이유는 나중에 알게 될 겁니다.

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

에약간구필다니에서 약간의 합니다.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
}

단순하게 유지하기 위해 바운스를 껐던 곳입니다.주요 설정은 스크롤 보기 및 테이블 보기에 대한 위임자이며, 먼저 테이블 보기 스크롤이 꺼지도록 하는 것입니다.

이것들은 필요합니다 그래서.scrollViewDidScroll:위임 메서드는 스크롤 뷰의 맨 아래에 도달하고 테이블 뷰의 맨 위에 도달하는 작업을 처리할 수 있습니다.이 방법은 다음과 같습니다.

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

위임 방법은 스크롤 보기가 맨 아래에 도달했을 때 탐지하는 것입니다.그러면 테이블 보기를 스크롤할 수 있습니다.또한 테이블 보기가 스크롤 보기가 다시 활성화된 맨 위에 도달하면 감지됩니다.

결과를 보여주기 위해 GIF를 만들었습니다.

enter image description here

더 효율적이고 버그가 없도록 다니엘의 답변을 수정했습니다.

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

전체 프로젝트는 https://gitlab.com/vineetks/TableScroll.git 에서 확인할 수 있습니다.

많은 시행착오 끝에, 이것이 저에게 가장 효과적인 것입니다.솔루션은 두 가지 요구 사항을 해결해야 합니다. 1) 사용할 스크롤 속성을 결정합니다. tableView 또는 scrollView?테이블 보기가 해당 테이블/내용의 맨 위에 도달할 때까지 스크롤 보기에 권한을 부여하지 않는지 확인합니다.

스크롤 뷰와 테이블 뷰를 비교하여 스크롤 뷰를 사용해야 하는지 확인하기 위해 테이블 뷰 바로 위의 UIV 뷰가 프레임 내에 있는지 확인했습니다.UIView가 프레임 내에 있는 경우 ScrollView에 스크롤 권한이 있어야 합니다.UIView가 프레임 내에 없으면 tableView가 전체 창을 차지하므로 스크롤 권한이 있어야 합니다.

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

    }

}

이것이 누군가에게 도움이 되기를 바랍니다!

여기 있는 어떤 대답도 저에게 완벽하게 먹히지 않았습니다.각각 고유의 미묘한 문제(스크롤 보기가 하단에 닿거나 스크롤 표시기가 올바르게 보이지 않을 때 반복적으로 스와이프해야 함)가 있었기 때문에 다른 답변을 제출하기로 했습니다.

Ole Begemann은 정확히 이것을 하는 것에 대해 훌륭한 글을 쓰고 있습니다. https://oleb.net/blog/2014/05/scrollviews-inside-scrollviews/

오래된 게시물임에도 불구하고, 그 개념은 여전히 현재 API에 적용됩니다.또한 그의 접근 방식 https://github.com/eyeem/OLEContainerScrollView 의 목표-C 구현이 유지되고 있습니다(Xcode 9 호환성이 있습니다.

중첩된 스크롤 문제로 인해 문제가 발생하는 경우 여기에서 해결할 수 있는 가장 간단한 방법이 있습니다.

  1. 디자인 화면으로 이동
  2. 스크롤 보기를 선택한 다음 스크롤 바운스를 비활성화합니다.
  3. 보기가 스크롤 보기 내부의 테이블 보기를 사용하는 경우 테이블 보기의 바운스도 비활성화합니다.
  4. 실행하여 해결되었는지 확인합니다.

스크롤 보기의 바운스를 비활성화하는 방법 확인

테이블 뷰의 스크롤 바운스를 비활성화하는 방법 확인

저도 이 문제로 고생하고 있었습니다.아주 간단한 해결책이 있습니다.

인터페이스 작성기:

  1. 단순 뷰 컨트롤러 생성
  2. 단순 보기를 추가하면, 그것은 우리의 헤더가 될 것이고,
    • 아래 예제의 빨간색 보기입니다.
    • 위, 왼쪽, 오른쪽에서 12px를 추가했고 고정 높이를 128px로 설정했습니다.
  3. PageViewController를 포함하여 헤더가 아닌 수퍼뷰로 제한되도록 합니다.

ib

이제 재미있는 부분이 있습니다. 추가하는 각 페이지에 대해 테이블 뷰에 위쪽에서 오프셋이 있는지 확인하십시오.바로 그거야.예를 들어 이 코드로 다음과 같은 작업을 수행할 수 있습니다(UITableViewController를 페이지로 사용하는 경우).

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let tables = viewControllers.compactMap { $0 as? UITableViewController }
    tables.forEach {
        $0.tableView.contentInset = UIEdgeInsets(top: headerView.bounds.height, left: 0, bottom: 0, right: 0)
        $0.tableView.contentOffset = CGPoint(x: 0, y: -headerView.bounds.height)
    }
}

테이블 뷰 내부의 스크롤 내부의 지저분한 스크롤, 대리인과의 충돌, 중복된 스크롤, 완벽하게 자연스러운 동작이 없습니다.머리글이 보이지 않으면 테이블 보기 배경색 때문일 수 있습니다.표 보기 아래에서 헤더를 볼 수 있도록 하려면 이 값을 clear로 설정해야 합니다.

저는 두 가지 선택지가 있다고 생각합니다.

스크롤 뷰와 기본 뷰의 크기를 알고 있으므로 스크롤 뷰가 맨 아래에 있는지 여부를 구분할 수 없습니다.

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

그래서 그것이 부딪혔을 때; 당신은 기본적으로 설정합니다.

[contentScrollView setScrollEnabled:NO];

테이블을 다른 방향으로 이동할 수 있습니다.

제가 생각하기에 더 정확한 또 다른 것은 당신의 관점에 제스처를 추가하는 것입니다.

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

따라서 제스처를 추가할 때 변경하여 활성 보기를 간단히 제어할 수 있습니다.setScrollEnabled에 시대에respondToTapGesture.

멋진 도서관 MXParalaxHeader를 찾았습니다.

스토리보드에서 방금 설정된UIScrollView에 등급을 매기다.MXScrollView그러면 마법이 일어납니다.

나는 이 수업을 나의 수업을 처리하기 위해 사용했습니다.UIScrollView내가 내장할 수 있는UIPageViewController컨이너뷰테 은 더 을 위해 할 수 .자세한 내용은 시차 헤더 뷰를 삽입할 수도 있습니다.

이는 또한이은관도를 합니다.Cocoapods그리고.Carthage

아래에 이미지를 첨부했는데, 이 이미지는 다음과 같습니다.UIView계층 구조.MXScrollView 계층 구조

스위프트 5

화면 크기가 다양하여 스크롤뷰 콘텐츠 오프셋(Y)을 보장할 수 없을 때 Vineet의 답변을 사용하는 데 어려움을 겪었습니다.이 문제를 해결하기 위해 tableView의 스크롤이 활성화될 때의 첫 번째 트리거 이벤트를 변경했습니다.

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if scrollView.bounds.contains(button.frame) {
            tableView.isScrollEnabled = true
        }

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

스크롤 보기.bounds.contains는 지정된 요소의 프레임이 scrollView의 표시 가능한 내용 내에 완전히 있는지 확인합니다.테이블 보기 아래에 있는 단추로 설정했습니다.당신은 이것을 당신의 테이블에 놓을 수 있습니다.tableView가 완전히 보이는 것이 유일한 조건인 경우 대신 VIEW의 프레임입니다.

tableView의 스크롤을 비활성화할 시기에 대한 원래 구현을 남겼는데 매우 잘 작동합니다.

정답으로 표시된 솔루션을 시도해 보았지만 제대로 작동하지 않았습니다.사용자는 스크롤을 위해 테이블 뷰를 두 번 클릭해야 하고, 그 후에는 전체 화면을 다시 스크롤할 수 없었습니다.그래서 viewDidLoad()에 다음 코드를 적용했습니다.

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

그리고 아래 코드는 조치의 구현입니다.

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

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

원하는 경우 한 가지 쉬운 방법은 상위 스크롤 보기를 일반 컨테이너 보기로 바꾸는 것입니다.컨테이너 뷰에 이동 제스처를 추가하면 첫 번째 뷰의 상단 제약 조건으로 재생하여 음수 값을 할당할 수 있습니다.페이지 뷰가 최상위에 도달할 경우 페이지 뷰의 하위 뷰 내용 오프셋에 해당 값을 할당할 수 있습니다.사용자가 컨테이너 보기에서 가장 보기가 높은 상태에서 테이블 보기를 사용할 수 있을 때까지 페이지 테이블 보기의 스크롤을 사용 불가능으로 유지하고 내용 오프셋을 설정하여 수동으로 스크롤할 수 있습니다.따라서 처음에는 페이지 뷰 높이가 아래쪽으로 접히거나 화면 밖으로 표시됩니다.나중에 아래로 스크롤하면 더 많은 공간이 확보되도록 확장됩니다.

프레임이 부족하면 제스처가 자동으로 응답을 중지합니다. 프레임이 탐색 막대 또는 컨테이너 뷰 외부의 다른 뷰에 표시됩니다.

제스처는 많은 앱에서 사용되는 사용자 대화형 전환의 핵심입니다.이것으로 일정 시간 동안 스크롤을 흉내낼 수 있습니다.

저의 경우 높이에 대한 제약 조건을 다음과 같이 사용합니다.

self.heightTableViewConstraint.constant = self.tableView.contentSize.height
self.scrollView.contentInset.bottom = self.tableView.contentSize.height

아래 코드는 나에게 잘 작동합니다. 스크롤해야 할 스크롤 및 테이블 보기 후에 헤더를 표시하고 싶었기 때문입니다.

그고리에서.ViewDidLoad더하다

 override func viewDidLoad() {
    super.viewDidLoad()
    mainScrollView.delegate = self
}

265를 위쪽 스크롤을 중지할 숫자로 변경


extension AccountViewController: UIScrollViewDelegate {
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        print(notebookTableView.contentOffset.y)
        
        if notebookTableView.contentOffset.y < 265 {
            if  notebookTableView.contentOffset.y > 0 {
                mainScrollView.setContentOffset(notebookTableView.contentOffset, animated: false)
                
            } else {
                mainScrollView.setContentOffset(CGPoint(x: 0.0, y: 0.0), animated: false)
            }
        } else {
            mainScrollView.setContentOffset(CGPoint(x: 0.0, y: 265), animated: false)
        }
    }
}
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);

강력한 힘이 필요할지도 모르지만, 셀 높이가 같다면 완벽하게 작동합니다. 그나저나, 저는 자동 레이아웃을 사용합니다.

테이블뷰(또는 컬렉션뷰 등)의 경우 스토리보드에서 임의의 높이를 설정하고 클래스에 대한 아웃렛을 만듭니다.필요한 경우 (viewDidLoad() 또는 ...)는 tableView가 스크롤할 필요가 없도록 tableView의 높이를 충분히 크게 설정합니다.(행 수를 미리 알아야 함)그러면 외부 스크롤 보기만 잘 스크롤됩니다.

언급URL : https://stackoverflow.com/questions/33292427/how-to-make-the-scroll-of-a-tableview-inside-scrollview-behave-naturally