인디노트
SwiftUI 에서 ScrollViewReader 를 사용하지 않고 List 의 맨 아래로 가는 방법은 없나? 본문
소스 팁/Objective C, Swift, iOS, macOS
SwiftUI 에서 ScrollViewReader 를 사용하지 않고 List 의 맨 아래로 가는 방법은 없나?
인디개발자 2023. 4. 5. 12:42ScrollViewReader 사용의 경우이고
import SwiftUI
struct ContentView: View {
@State var items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var body: some View {
VStack {
ScrollViewReader { proxy in
List(items, id: \.self) { item in
Text("Item \(item)")
.id(item)
}
.onAppear {
// Scroll to the last item when the list appears
withAnimation {
proxy.scrollTo(items.last, anchor: .bottom)
}
}
}
Button(action: {
// Add an item to the list and scroll to the bottom
let newItem = items.count + 1
items.append(newItem)
withAnimation {
proxy.scrollTo(newItem, anchor: .bottom)
}
}, label: {
Text("Add Item")
})
}
}
}
이런 방법?
import SwiftUI
struct ContentView: View {
@State private var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
var body: some View {
VStack {
List(items, id: \.self) { item in
Text(item)
}
.onAppear {
// Scroll to the last item when the list appears
withAnimation {
scrollToLastItem()
}
}
Button("Add Item") {
// Add a new item to the list and scroll to the bottom
let newItem = "Item \(items.count + 1)"
items.append(newItem)
withAnimation {
scrollToLastItem()
}
}
}
}
func scrollToLastItem() {
// Scroll to the last item in the list
guard let lastItem = items.last else { return }
withAnimation {
let index = items.index(of: lastItem)!
let lastIndex = items.index(before: items.endIndex)
UITableView.appearance().separatorStyle = .none
UITableView.appearance().backgroundColor = .clear
UITableView.appearance().separatorColor = .clear
UITableView.appearance().showsVerticalScrollIndicator = false
UITableView.appearance().showsHorizontalScrollIndicator = false
UITableViewCell.appearance().backgroundColor = .clear
UITableView.appearance().isScrollEnabled = false
UITableView.appearance().allowsSelection = false
UITableView.appearance().isHidden = true
List(selection: .constant("")) {
ForEach(items, id: \.self) { item in
Text(item)
}
}
.frame(maxHeight: .infinity)
.onAppear {
UITableView.appearance().isScrollEnabled = true
UITableView.appearance().allowsSelection = true
UITableView.appearance().isHidden = false
}
.onChange(of: items) { newValue in
UITableView.appearance().isScrollEnabled = true
UITableView.appearance().allowsSelection = true
UITableView.appearance().isHidden = false
}
.introspectTableView(customize: { tableView in
let indexPath = IndexPath(row: index, section: 0)
tableView.scrollToRow(at: indexPath, at: .bottom, animated: true)
})
}
}
}
extension View {
func introspectTableView(customize: @escaping (UITableView) -> ()) -> some View {
return introspect(selector: TargetViewSelector.ancestorOrSibling(isTableCell).isTableView, customize: customize)
}
}
func isTableCell(_ element: AnyViewType.Type) -> Bool {
let className = NSStringFromClass(element)
return className == "_UITableViewCellContentView" || className == "UITableViewCellContentView"
}
또다른 방법...
import SwiftUI
struct ContentView: View {
@State var items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var body: some View {
VStack {
List(items, id: \.self) { item in
Text("Item \(item)")
.id(item)
}
.onAppear {
// Scroll to the last item when the list appears
scrollToLastItem()
}
Button(action: {
// Add an item to the list and scroll to the bottom
let newItem = items.count + 1
items.append(newItem)
scrollToLastItem()
}, label: {
Text("Add Item")
})
}
}
func scrollToLastItem() {
withAnimation {
guard let lastItem = items.last else { return }
let lastIndex = items.count - 1
let lastItemID = AnyHashable(lastItem)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
// Add a small delay to make sure the list is fully rendered
// before scrolling to the last item
if let lastItemRect = getRectForLastItem() {
// Scroll to the last item using scrollTo method
UITableView.appearance().scrollToRow(
at: IndexPath(row: lastIndex, section: 0),
at: .bottom, animated: true)
}
}
}
}
func getRectForLastItem() -> CGRect? {
guard let lastItem = items.last else { return nil }
let lastItemID = AnyHashable(lastItem)
return UITableView.appearance().rectForRow(
at: IndexPath(row: items.count - 1, section: 0))
}
}
이런 방법? iOS 에서는 ScrollViewReader 가 사용할 수 없다는데... 이게 뭐지?
Type '()' cannot conform to 'View'
scrollTo 메소드는 ScrollView 내부의 모든 뷰에 고유한 ID를 제공하므로, List의 각 요소에 ID를 부여할 필요가 없습니다. 대신, List의 마지막 요소를 scrollTo 메소드로 전달하고, anchor 매개 변수를 .bottom으로 설정하여 List의 맨 아래쪽으로 스크롤합니다.
struct ContentView: View {
@State private var items = ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5"]
var body: some View {
VStack {
List(items, id: \.self) { item in
Text(item)
}
Button("Scroll to Bottom") {
withAnimation {
scrollToBottom()
}
}
}
}
private func scrollToBottom() {
guard let lastItem = items.last else { return }
withAnimation {
// The anchor parameter determines where the item is positioned after scrolling
// .bottom means the item will be positioned at the bottom of the visible area
ScrollViewReader { scrollView in
scrollView.scrollTo(lastItem, anchor: .bottom)
}
}
}
}
반응형
'소스 팁 > Objective C, Swift, iOS, macOS' 카테고리의 다른 글
macOS 에서 objective c 로 windowDidLoad 이벤트 처리시 현재 윈도우의 위치를 화면의 중앙으로 이동하는 방법 (0) | 2023.04.05 |
---|---|
xcode 에서 직접 app 에 서명 (0) | 2023.04.04 |
MechanismInvoke 를 구현하는 샘플 (0) | 2023.03.31 |
SFAuthorizationPluginView (0) | 2023.03.31 |
macOS 관리자 권한 얻어오기 (0) | 2023.03.29 |
Comments