[SwiftUI] scrollTargetLayout과 ScrollTargetBehavior

2024. 8. 16. 14:31·🍎 Dev/SwiftUI

 

 

iOS 17이전까지 ScrollView를 활용할 때 제한적인 부분이 많았다.

 

특히 오프셋을 직접 구현해서 페이징 기능을 커스텀으로 만들어야한다는 불편함이 있었는데, 애플에서 scrollTargetLayout과 scrollTargetBehavior를 iOS 17때 추가해주어서 이제 코드 단 두 줄로 구현이 가능해졌다.

 

 

우선, 해당 기능에 대해 애플문서로 간략하게 알아보자.

 

 

 

scrollTargetLayout은 스크롤 타겟으로 설정할 레이아웃을 정하는 역할을 해준다. ...라고 쓰려니까 이게 무슨 소리야, 싶은데 이 메서드는 스크롤 액션이 이루어질 때 어떤 레이아웃을 기점으로 스크롤이 이루어질지 정해준다.

 

그리고 scrollTargetLayout은 단독으로 쓰이지 않고, 대신 ViewAlignedScrollTargetBehavior(ScrollTargetBehavior)와 함께 사용해서 스크롤 뷰가 특정 뷰나 콘텐츠에 정확하게 정렬될 수 있도록 돕는 역할을 수행한다.

 

 

 

ScrollTargetBehavior는 스크롤가능한 뷰의 스크롤 동작을 정의해주는 프로토콜이다. Paging Behavior와 View Aligned Behavior 방식이 있다.

 

활용은 아래와 같이 할 수 있다.

 

 

 

 

 


 

 

 

 

 

 

 

1-1. scrollView로 UI를 잡아준다.

 

 

 

 

구현코드:

 

import SwiftUI

struct CarouselViewSwiftUI: View {

    /// Carousel에서 보여줄 색상 배열
    let colors: [Color] = [.red, .blue, .green, .pink, .purple]

    /// 아이템 사이 간격
    let itemSpacing: CGFloat = 12
    /// 아이템 너비
    let itemWidth = 200

    var body: some View {
        
        ScrollView(.horizontal) {
            
            HStack(alignment: .center, spacing: itemSpacing) {
                
                ForEach(colors, id: \.self) { color in
                    color
                        .frame(width: CGFloat(itemWidth), height: 300)
                        .cornerRadius(12)
                } // forEach
                
            } // hStack
            .padding(.leading, 24)

        } // scrollView
        .scrollIndicators(.hidden)
        .frame(height: 300)
        
    } // body
} // view

 

 

여기까지는 우리가 아는 스크롤뷰다. 위 코드처럼 구현했을 때 드래그하면 스르륵~ 넘어간다.

 

 

 

1-1. Paging Behavior

 

 

 

 

구현코드:

 

import SwiftUI

struct CarouselViewSwiftUI: View {

    /// Carousel에서 보여줄 색상 배열
    let colors: [Color] = [.red, .blue, .green, .pink, .purple]

    /// 아이템 사이 간격
    let itemSpacing: CGFloat = 12
    /// 아이템 너비
    let itemWidth = 200

    var body: some View {
        
        ScrollView(.horizontal) {
            
            HStack(alignment: .center, spacing: itemSpacing) {
                
                ForEach(colors, id: \.self) { color in
                    color
                        .frame(width: CGFloat(itemWidth), height: 300)
                        .cornerRadius(12)
                } // forEach
                
            } // hStack
            .padding(.leading, 24)

        } // scrollView
        .scrollIndicators(.hidden)
        .scrollTargetBehavior(.paging) // <<<<< 스크롤 방식 정의
        .frame(height: 300)
        
    } // body
} // view

 

 

.scrollTargetBehavior(.paging)을 scrollView에 추가해주면, 페이징 처리가 된다. 스르륵~ 넘어가던게 스륵탁! 하며 넘어가는 걸 확인할 수 있다. 해당 모디파이어는 스크린 너비가 스크롤 오프셋이 되도록 해준다.

 

그런데..... 단순히 페이징 기능이 필요하면 tabView를 쓰는 게 더 낫지 않을까... 하는 생각도 들었다.

 

 

 

1-2. ViewAligned

 

 

구현코드:

 

import SwiftUI

struct CarouselViewSwiftUI: View {

    /// Carousel에서 보여줄 색상 배열
    let colors: [Color] = [.red, .blue, .green, .pink, .purple, .gray, .mint]

    /// 아이템 사이 간격
    let itemSpacing: CGFloat = 12
    /// 아이템 너비
    let itemWidth = 200

    var body: some View {
        
        ScrollView(.horizontal) {
            
            HStack(alignment: .center, spacing: itemSpacing) {
                
                ForEach(colors, id: \.self) { color in
                    color
                        .frame(width: CGFloat(itemWidth), height: 300)
                        .cornerRadius(12)

                } // forEach
                
            } // hStack
            .padding(.leading, 24)
            
        } // scrollView
        .scrollIndicators(.hidden)
        .scrollTargetBehavior(.viewAligned) // <<<<< 스크롤 방식 정의
        .frame(height: 300)
        
    } // body
} // view

 

 

.scrollTargetBehavior(.viewAligned)로 변경하면, paging으로 설정했을 때와 달리 부드럽게 스크롤이 동작되는 걸 확인할 수 있다. 그런데 랜덤하게 멈추는데, 이 때 scrollTargetLayout()를 사용하면 아이템(color)별로 멈추게 구현할 수 있다.

 

 

 

구현코드:

 

import SwiftUI

struct CarouselViewSwiftUI: View {

    /// Carousel에서 보여줄 색상 배열
    let colors: [Color] = [.red, .blue, .green, .pink, .purple, .gray, .mint]

    /// 아이템 사이 간격
    let itemSpacing: CGFloat = 12
    /// 아이템 너비
    let itemWidth = 200

    var body: some View {
        
        ScrollView(.horizontal) {
            
            HStack(alignment: .center, spacing: itemSpacing) {
                
                ForEach(colors, id: \.self) { color in
                    color
                        .frame(width: CGFloat(itemWidth), height: 300)
                        .cornerRadius(12)

                } // forEach
                
            } // hStack
            .scrollTargetLayout() // <<<<< 추가
            .padding(.leading, 24)
            
        } // scrollView
        .scrollIndicators(.hidden)
        .scrollTargetBehavior(.viewAligned) // <<<<< 스크롤 방식 정의
        .frame(height: 300)
        
    } // body
} // view

 

 

 

.scrollTargetBehavior에서 viewAligned를 해주면, 아이템의 오프셋이 가운데로 오도록 해준다. (나는 padding을 추가하고 있어서 조금 다르게 구현되었지만..!)

 

 

 


 

References

 

https://developer.apple.com/documentation/swiftui/view/scrolltargetlayout(isenabled:)

 

scrollTargetLayout(isEnabled:) | Apple Developer Documentation

Configures the outermost layout as a scroll target layout.

developer.apple.com

 

https://developer.apple.com/documentation/swiftui/view/scrolltargetbehavior(_:)

 

scrollTargetBehavior(_:) | Apple Developer Documentation

Sets the scroll behavior of views scrollable in the provided axes.

developer.apple.com

 

https://medium.com/@myshkinasasha/swiftui-scrollview-with-paging-ios17-and-below-a293766e19e8

 

SwiftUI ScrollView with paging iOS17 and below

From iOS17, SwiftUI is finally getting modifier to add ScrollView paging behaviour 🎉 FI-NA-LLY.

medium.com

 

저작자표시 (새창열림)

'🍎 Dev > SwiftUI' 카테고리의 다른 글

[SwiftUI] ObservableObject, ObservedObject, Published  (6) 2024.10.07
[SwiftUI] Navigation 1  (0) 2024.09.24
[SwiftUI] Property Wrapper 총정리  (0) 2024.07.26
[SwiftUI] Frame  (0) 2024.07.22
[SwiftUI] @State  (0) 2024.05.22
'🍎 Dev/SwiftUI' 카테고리의 다른 글
  • [SwiftUI] ObservableObject, ObservedObject, Published
  • [SwiftUI] Navigation 1
  • [SwiftUI] Property Wrapper 총정리
  • [SwiftUI] Frame
Callie_
Callie_
  • Callie_
    CalliOS
    Callie_
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 🍎 APPLE
      • 🍎 Dev
        • Swift
        • UIKit
        • SwiftUI
        • Issue
        • 구현
      • 🍎 Design
        • HIG
      • ⚙️ CS
      • 💡 알고리즘
        • 프로그래머스
        • 백준
      • 🏟️ 직관로그 (출시앱)
        • 업데이트
      • 🌱 SeSAC iOS 3기
  • 블로그 메뉴

    • 홈
    • 태그
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    stroyboard
    assets
    .fullScreen
    SeSAC
    Snapshot
    IBOutlet
    DidEndOnExit
    clipsToBound
    ios
    Info탭
    SwiftUI
    화면전환
    modalPresentStyle
    TableViewCell
    Entry Point
    DiffableDataSource
    CocoaTouchFramework
    .OverFullScreen
    생명주기
    Enum
    addTarget
    layer.shadow
    IBAction
    네트워크통신
    apply
    Swift
    CustomView
    TapGestureRecognizer
    cornerradius
    diffable
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
Callie_
[SwiftUI] scrollTargetLayout과 ScrollTargetBehavior
상단으로

티스토리툴바