본문 바로가기

iOS/BugReport

SwiftUI iOS 15에서 ScrollViewReader가 되지 않는 문제

나는 보통 iOS14의 테스트 기기에서 작업을 하는데 그 이유는 기존 버전에서 잘 동작하면 새 버전에서는 반드시 된다는 전제가 있었고 그 전에 iOS15에서 정상 동작하던게 이전 버전에서 버그가 발현되는 문제가 있었던 적이 있어서 가능하면 구 버전 기기로 테스트하는 경향이 있다.

 

그런데!! 최근 작업하면서 iOS14인 나의 테스트 기기에서는 정상동작했지만 iOS15에서 버그가 발생한 경우가 있었다. 오늘은 이번에 발현하고 수정한 ScrollViewReader에서 scrollTo(:to:)이 실행될 때 버그를 분석하고 수정한 것을 공유하고 앞으로도 버그가 있을 때마다 기록 해 두어야겠다.. 🤬

 

💫 Problem

https://developer.apple.com/documentation/swiftui/scrollviewreader

 

Apple Developer Documentation

 

developer.apple.com

우선 ScrollViewReader의 샘플코드는 위에서 참고했으며 테스트를 위해 약간 수정했다.

struct ContentView: View {
    @Namespace var topID
    @Namespace var bottomID
    
    var body: some View {
        ScrollViewReader { proxy in
            ScrollView {
                VStack {
                    VStack(spacing: 16) {
                        Text("뭐 대충 대충 타이틀")
                            .font(.largeTitle)
                        
                        Text("뭐 대충 대충 서브 타이틀")
                            .font(.title)
                        
                        Text("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eu feugiat pretium nibh ipsum consequat. Nunc scelerisque viverra mauris in aliquam sem fringilla. Praesent elementum facilisis leo vel fringilla est ullamcorper eget nulla. Phasellus faucibus scelerisque eleifend donec pretium. Erat velit scelerisque in dictum non consectetur. Posuere urna nec tincidunt praesent semper feugiat nibh. Lacus laoreet non curabitur gravida. Urna molestie at elementum eu. Venenatis urna cursus eget nunc scelerisque viverra mauris. Donec enim diam vulputate ut pharetra.")
                    }
                    .padding()
                    
                    VStack {
                        Button("Scroll to Bottom") {
                            withAnimation {
                                proxy.scrollTo(bottomID)
                            }
                        }
                        .id(topID)
                    
                        VStack(spacing: 0) {
                            ForEach(0..<100) { i in
                                color(fraction: Double(i) / 100)
                                    .frame(height: 32)
                            }
                        }
                        
                        Button("Top") {
                            withAnimation {
                                proxy.scrollTo(topID)
                            }
                        }
                        .id(bottomID)
                    }
                }
            }
        }
    }
    
    func color(fraction: Double) -> Color {
        Color(red: fraction, green: 1 - fraction, blue: 0.5)
    }
}

 

기존 애플 문서에 있는 샘플 코드를 작성하고 상단에 뷰를 추가하였을 때 아래와 같은 버그가 생겼다.

 

iOS 14)

iOS14에서는 원하는대로 동작한다.

iOS 15)

iOS15에서는 버그가 있다.

 

💫 Analysis

사실 애플 샘플코드를 가져와서 실험하고 여러가지 경우를 두고 실험했는데도 정상적으로 동작해서 당혹했지만 버그가 나오는 구조를 찾을 수 있었다.

 

case1) 🐛

더보기

ScrollViewReader { proxy in

    ScrollView {

        VStack {

            VStack {}


            VStack {} // 👈 여기에 scrollTo가 있다.
        }
    }
}

상단 샘플코드와 같은 구조. 상단 뷰의 높이(VStack)만큼 위로 말려 올라가는 버그가 있다.

 

case2) 🐛

더보기

ScrollViewReader { proxy in

    ScrollView {

        VStack {}


        VStack {} // 👈 여기에 scrollTo가 있다.
    }
}

case1 코드에서 ScrollView에 공통으로 있던 VStack을 제거하여 계층을 한 단계 낮춰도 똑같이 버그가 발생했다.

 

case3) 🐛

더보기

ScrollViewReader { proxy in

    ScrollView {

        VStack {

            Text("Title")

            Text("SubTitle")

            Text("Content")


            VStack {} // 👈 여기에 scrollTo가 있다.
        }
    }
}

case1 코드에서 상단 VStack에 있는 코드블럭들을 바깥으로 꺼냈을 때도 여전히 버그가 발생했다.

 

case4) 

더보기

ScrollViewReader { proxy in

    ScrollView {

        VStack {

            VStack {}

 

            Button("Scroll to Bottom") {

                withAnimation {

                    proxy.scrollTo(bottomID)

                }

            }

            .id(topID)

        

            VStack(spacing: 0) {

                ForEach(0..<100) { i in

                    color(fraction: Double(i) / 100)

                        .frame(height: 32)

                }

            }

            

            Button("Top") {

                withAnimation {

                    proxy.scrollTo(topID)

                }

            }

            .id(bottomID)

        }
    }
}

case1 코드에서 scrollTo가 있는 코드 블럭을 VStack에서 빼냈을 때는 정상 동작하였다.

 

case5) 

더보기

ScrollViewReader { proxy in

    ScrollView {

        VStack {

            VStack {}


            Group {} // 👈 여기에 scrollTo가 있다.
        }
    }
}

case1 코드에서 VStack을 Group으로 바꾸면 정상동작한다!! alignment와 spacing을 못 쓰지만 뷰들을 묶을 땐 VStack대신 Group으로 리팩토링하여 사용하면 될 것 같다.

물론 Group안에 VStack을 넣어도 이전처럼 버그가 발생한다.

 

case6) 🐛

더보기

ScrollViewReader { proxy in

    ScrollView {

        VStack {

            VStack {}


            VStack {} // 👈 여기에 scrollTo(bottomID)가 있다.

 

            Button("Top") {

                withAnimation {

                    proxy.scrollTo(topID)

                }

            }

            .id(bottomID)

        }
    }
}

case1 코드에서 하단 VStack에 있는 Button("Top")만 VStack 바깥으로 뺐을때는 바텀으로 가는 버튼은 버그가 없었지만 탑으로 갈 때는 버그가 잔존했었다.

 

💫 Conclusion

일단iOS 15에서 이같은 기능을 구현할 때는 위 중에 5, 6케이스를 적용해서 사용하면 될 것같다..!