ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UIKit SwiftUI에서 자동 스크롤
    개발/Swift 2025. 4. 8. 15:24

    UIKit 자동 스크롤

    자동(동적) 스크롤 하기 위해 uikit 에서도 scrollRectToVisible 제공해줌

      layoutIfNeeded() // Init 직후에 아직 내부 레이아웃이 안잡혀서 호출 필요
      scrollRectToVisible(button.frame, animated: true)  // ScrollView 기능
    
    

    근데 이게 스유 처럼 가운데로 보내주는게 아니라

    그냥 보이게만 해준다.

    스크롤 하려는 저 button 이 화면에 아예 안보이면 잘 동작하는데

    만약 왼쪽 찔끔 나온 상태라면 움직이지 않는 문제가 있음

    따라서 별도로 스크롤을 계산하여 setContentOffset 호출이 필요함

    layoutIfNeeded()
    
    let buttonCenterX = button.center.x 
    
    DispatchQueue.main.async { // view 렌더링이 끝난 이후에 동작해야 알맞은 self.bounds 가 받아와짐
        var targetOffsetX = buttonCenterX - self.bounds.width / 2 // 버튼이 스크롤 영역 가운데보다 왼쪽이면 음수
        targetOffsetX = max(0, min(targetOffsetX, self.contentSize.width - self.bounds.width)) // 컨텐츠 사이즈 작으면 음수
        self.setContentOffset(CGPoint(x: targetOffsetX, y: 0), animated: true)
    }
    

     

    SwiftUI 자동 스크롤

    ScrollViewReader 를 사용하면 쉽게 동적 스크롤 기능 구현가능

    ScrollView(.horizontal, showsIndicators: false) {
        ScrollViewReader { proxy in
    			ForEach(categories, id: \\.id) { category in
              Button(action: {
                  onSelectCategory(category.id)
              }) {
                  Text(category.title)
    					}.id(category.id) // id 지정
    			}
    	}
    }
    

    버튼에 지정된 id 를 기준으로 스크롤이 가능함

    .onChange(of: selectedCategoryId, perform: { id in
          withAnimation {
              proxy.scrollTo(id, anchor: .center)
          }
      })
    

    selectedCategoryId 값이 바뀔때 마다 onChange 블록이 호출되고

    proxy.scrollTo 를 활용해 스크롤 이 가능함

     

    anchor: .center 를 통해 최대한 가운데에 위치하도록 쉽게 구현할수 있음 ( 이외 .leading .trailing 이 있음)

    스크롤이 끝나서 가운데로 갈수없으면 갈수 있는 만큼만 스크롤 됨

     

    onChange 의 특성상 selectedCategoryId 이 바뀌는 경우에만 호출되는데

    만약 init 시점에 selectedCategoryId 지정되었고 바뀌지 않는다면 view 가 렌더링 된 이후에 값이 바뀌지 않았으므로

    진입과 동시에 스크롤 되지는 않는다.

    만약 따로 처리를 하려면 onAppear 에서 같은 방식으로 scroll 해야함

    .onAppear {
    	withAnimation {
        proxy.scrollTo(selectedCategoryId, anchor: .center)
    	}
    }
    

    댓글

Designed by Tistory.