{"id":343516,"date":"2023-01-04T15:05:35","date_gmt":"2023-01-04T15:05:35","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=343516"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=343516","title":{"rendered":"<span>SwiftUI ScrollView \u0438 \u043f\u0430\u0440\u0430\u043b\u043b\u0430\u043a\u0441 \u0431\u0435\u0437 \u0442\u043e\u0440\u043c\u043e\u0437\u043e\u0432<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u041d\u0438\u043a\u043e\u043b\u0430\u0439, \u044f iOS-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434\u043e \u043c\u043d\u043e\u0439 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u043d\u0430 \u0437\u0430\u0434\u043d\u0435\u043c \u043f\u043b\u0430\u043d\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442. \u041f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0439, \u043d\u043e \u0441 \u0437\u0430\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e &#8212; \u043a\u0430\u043a \u043e\u0442\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0444\u043e\u043d \u0432 \u043c\u0443\u043b\u044c\u0442\u0444\u0438\u043b\u044c\u043c\u0430\u0445 \u0438\u043b\u0438 \u0438\u0433\u0440\u0430\u0445.<\/p>\n<h2>\u0418\u0442\u0430\u043a, \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c.<\/h2>\n<p>\u0412 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u043c UIScrollView \u0438\u0437 UIKit \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollviewdelegate\" rel=\"noopener noreferrer nofollow\">UIScrollViewDelegate<\/a> &#8212; \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollviewdelegate\/1619392-scrollviewdidscroll\" rel=\"noopener noreferrer nofollow\">scrollViewDidScroll(_ scrollView: UIScrollView)<\/a> \u0441\u043a\u0430\u0436\u0435\u0442 \u043d\u0430\u043c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043c\u0435\u0441\u0442\u0438\u043b\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442. \u041d\u043e \u0432 SwiftUI ScrollView \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0434\u0435\u043b\u0435\u0433\u0430\u0442\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u043e\u0432\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438.<\/p>\n<p>\u042f \u043d\u0430\u0448\u0451\u043b \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u0435 &#8212; <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/geometryreader\" rel=\"noopener noreferrer nofollow\">GeometryReader<\/a> \u0432\u043d\u0443\u0442\u0440\u0438 ScrollView:<\/p>\n<pre><code class=\"swift\">struct ContentView: View {     @State private var scrollOffset = CGFloat(0)      var body: some View {         ScrollView {             ZStack {                 Color.green                     .opacity(0.5)                     .frame(height: UIScreen.main.bounds.height * 3)                  GeometryReader { proxy in                     let offset = proxy.frame(in: .named(\"scroll\")).minY                     scrollOffset = offset                 }             }         }     } }<\/code><\/pre>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e GeometryReader \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435. \u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043d\u0430 \u0442\u0430\u043a\u043e\u0439 \u043a\u043e\u0434 \u0440\u0443\u0433\u0430\u0435\u0442\u0441\u044f<\/p>\n<pre><code>\"Type '()' cannot conform to 'View'<\/code><\/pre>\n<p> \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/preferencekey\" rel=\"noopener noreferrer nofollow\">PreferenceKey<\/a>:<\/p>\n<pre><code class=\"swift\">struct ScrollOffsetPreferenceKey: PreferenceKey {     static var defaultValue: CGFloat = 0      static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {         value = nextValue()     } }<\/code><\/pre>\n<p>\u041c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 GeometryReader:<\/p>\n<pre><code class=\"swift\">GeometryReader { proxy in     let offset = proxy.frame(in: .named(\"scroll\")).minY     Color.clear.preference(key: ScrollOffsetPreferenceKey.self,                            value: offset) }<\/code><\/pre>\n<p>\u042d\u0442\u043e \u0432\u044b\u043d\u043e\u0441 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0432 PreferenceKey. \u041a\u0440\u043e\u043c\u0435 \u044d\u0442\u043e\u0433\u043e, \u043d\u0430\u0434\u043e \u0435\u0449\u0451 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e:<\/p>\n<pre><code class=\"swift\">.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in     scrollOffset = value }<\/code><\/pre>\n<p>\u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043d\u0435 \u0440\u0443\u0433\u0430\u0435\u0442\u0441\u044f. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ul>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u0447\u0442\u043e \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0442\u044c, \u0438 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e. \u0421\u0440\u0430\u0437\u0443 \u0432\u044b\u043d\u043e\u0441\u0438\u043c \u0435\u0433\u043e \u0438\u0437 <code>body:<\/code><\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"swift\">var body: some View {     VStack {     ScrollView {         ZStack {             VStack {                 scrollViewContentBody             }             .opacity(0.75)                        GeometryReader { proxy in                 let offset = proxy.frame(in: .named(\"scroll\")).minY                 Color.clear.preference(key: ScrollOffsetPreferenceKey.self,                                        value: offset)             }         }     }     .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in         scrollOffset = value     } }  @ViewBuilder private var scrollViewContentBody: some View {     Text(\"Lorem ipsum\")         .font(.largeTitle)         .padding(16)      separator      Text(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\")         .font(.title)         .padding(16)      separator      Text(\"At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.\")         .font(.title)         .padding(16)     separator }  private var separator: some View {     Color.gray         .frame(height: 1 \/ UIScreen.main.scale)         .padding(16) }<\/code><\/pre>\n<ul>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0435\u0431\u0430\u0436\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"swift\">var body: some View {     VStack {         Text(\"\\(scrollOffset)\") \/\/ &lt;-- \u044d\u0442\u043e\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u043d\u0430\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u044f         ScrollView {             \/\/ ...<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0421\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/webt\/uz\/jf\/f9\/uzjff9b-k-m_h8eylkj7nmwlcfk.gif\" width=\"278\" height=\"500\" data-src=\"https:\/\/habrastorage.org\/webt\/uz\/jf\/f9\/uzjff9b-k-m_h8eylkj7nmwlcfk.gif\"\/><figcaption><\/figcaption><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0442\u0441\u044f &#8212; \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043b\u043e\u0432\u0438\u0442\u0441\u044f. \u041c\u0435\u043d\u044f\u0435\u043c \u043f\u0440\u043e\u0431\u043d\u0438\u043a\u0438 \u043d\u0430 \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u043a\u043e\u0434.<\/p>\n<details class=\"spoiler\">\n<summary>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0432\u044c\u044e\u0448\u043a\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">struct ScrollOffsetPreferenceKey: PreferenceKey {     static var defaultValue: CGFloat = 0      static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {         value = nextValue()     } }  struct RootView: View {         @State private var scrollOffset = CGFloat(0)     @State private var rate: Decimal? = 3.8          \/\/ MARK: - Vars          private var scrolledEnoughToShowTopBar: Bool {         -scrollOffset > -90     }          private var topBarHeight: CGFloat {         UIApplication.shared.safeAreaInsets.top + 42     }      private var topBackgroundOffset: CGFloat {         let bounds = UIScreen.main.bounds         return scrollOffset \/ 10 - bounds.height \/ 8     }      private var topImageOffset: CGFloat {         let bounds = UIScreen.main.bounds         return scrollOffset \/ 5 - bounds.height \/ 20     }      \/\/ MARK: - UI      @ViewBuilder     var body: some View {         let screenBounds = UIScreen.main.bounds         ZStack(alignment: .top) {             AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/8856514\/pexels-photo-8856514.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))                 .blur(radius: 4)                 .frame(width: screenBounds.width, height: screenBounds.height)                 .aspectRatio(contentMode: .fill)                 .clipped()              Group {                 topBackgroundBody                 EntryInfoView(imageOffset: topImageOffset)                 scrollBody.padding(.top, -200)             }         }         .ignoresSafeArea(.all, edges: [.top, .bottom])     }      private var topBackgroundBody: some View {         AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/10288317\/pexels-photo-10288317.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))             .resizable()             .aspectRatio(contentMode: .fill)             .frame(width: UIScreen.main.bounds.width,                    height: UIScreen.main.bounds.height \/ 1.5)             .aspectRatio(contentMode: .fill)             .clipped()             .offset(y: topBackgroundOffset)     }      @ViewBuilder     private var scrollBody: some View {         let size = UIScreen.main.bounds         ScrollView {             LazyVStack {                 Color.clear                     .frame(width: size.width, height: size.height - 40)                 ZStack(alignment: .top) {                     scrollContentBody                      GeometryReader { proxy in                         let offset = proxy.frame(in: .named(\"scroll\")).minY                         Text(\"\\(offset)\")                         Color.clear.preference(key: ScrollOffsetPreferenceKey.self, value: offset)                     }                 }                 Color.clear                     .frame(width: size.width,                            height: UIApplication.shared.safeAreaInsets.bottom)             }         }         .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in             scrollOffset = value         }     }          private var scrollContentBody: some View {         LazyVStack(spacing: 0) {             EntryScrollHeader()              Color.green.opacity(0.5)                 .frame(height: 200)              Color.red.opacity(0.5)             .frame(height: 1000)         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">struct EntryScrollHeader: View {     var body: some View {         VStack(spacing: 0) {             HStack {                 VStack {                     Text(\"Description\")                 }                  Spacer()                  Button {                  } label: {                     Image(systemSymbol: .bookmark)                         .foregroundColor(.black.opacity(0.25))                         .font(.system(size: 25, weight: .regular, design: .default))                 }             }             .padding(.bottom, 4)              HStack {                 VStack {                     Text(\"Lorem\")                     Text(\"Ipsum\")                 }                 Spacer()             }          }         .frame(height: 76)         .padding([.leading, .trailing], 15)         .padding(.top, 12)         .background(Color.white)     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0418 \u0432\u0435\u0440\u0445\u043d\u044f\u044f \u0432\u044c\u044e\u0448\u043a\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">struct EntryInfoView: View {     private var imageOffset: CGFloat      init(imageOffset: CGFloat) {         self.imageOffset = imageOffset     }      @ViewBuilder     var body: some View {         let screenBounds = UIScreen.main.bounds          ZStack(alignment: .topLeading) {             HStack {                 infoBody                  cardsBody                     .frame(height: screenBounds.height \/ 4)                     .padding(8)             }             .padding(16)         }         .offset(y: imageOffset)     }      @ViewBuilder     private var infoBody: some View {         let screenBounds = UIScreen.main.bounds         let rating = Decimal(4.2)          VStack(spacing: 0) {             Text(rating.ratingString)                 .font(.system(size: 24))                 .padding(.top, 8)              Text(\"49,849\")                 .font(.system(size: 10))                 .padding(.top, 4)              EntryLittleStarsView(rating: rating)                 .foregroundColor(.orange)                 .padding(4)         }         .frame(minHeight: screenBounds.width \/ 3)         .background(Color.white)         .cornerRadius(8)     }      private var cardsBody: some View {         GeometryReader { proxy in             let size = CGSize(width: proxy.size.width - 48,                               height: proxy.size.height - 48)             let imageSize = CGSize(width: proxy.size.width - 64,                                    height: proxy.size.height - 64)             ZStack {                 cardBody(size: size, color: .red) {                     ZStack {                         Color.white                          AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/10136037\/pexels-photo-10136037.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))                             .resizable()                             .blur(radius: 2)                             .aspectRatio(contentMode: .fill)                             .frame(width: imageSize.width, height: imageSize.height)                             .clipped()                             .cornerRadius(12)                     }                 }                 .offset(y: 48)                  cardBody(size: size, color: .green) {                     ZStack {                         Color.white                          AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/10243803\/pexels-photo-10243803.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))                             .resizable()                             .blur(radius: 2)                             .aspectRatio(contentMode: .fill)                             .frame(width: imageSize.width, height: imageSize.height)                             .clipped()                             .cornerRadius(12)                     }                 }                 .offset(x: 24, y: 24)                  cardBody(size: size, color: .blue) {                     ZStack {                         Color.white                          AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/10278313\/pexels-photo-10278313.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))                             .resizable()                             .aspectRatio(contentMode: .fill)                             .frame(width: imageSize.width, height: imageSize.height)                             .clipped()                             .cornerRadius(12)                     }                 }                 .offset(x: 48)             }         }     }      private func cardBody&lt;Content: View>(size: CGSize, color: Color, @ViewBuilder content: () -> Content) -> some View {         ZStack {             color                 .opacity(0.75)                 .frame(width: size.width, height: size.height)                 .cornerRadius(16)             content()                 .frame(width: size.width - 8,                        height: size.height - 8)                 .cornerRadius(14)         }         .shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 2)     }      private var priceBody: some View {         VStack {             ZStack {                 Image(systemSymbol: .circleSquare)                 HStack(alignment: .top, spacing: 0) {                     Text(\"$\")                         .font(.system(size: 9))                     Text(\"497\")                         .font(.system(size: 20))                 }             }             HStack(spacing: 0) {                 Image(systemSymbol: .plusMessage)                 Text(\"Average price\")             }             .font(.system(size: 10))             .opacity(0.25)         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c<\/summary>\n<div class=\"spoiler__content\">\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/61ab737e6a73daf6662a1c64\" data-style=\"\" id=\"61ab737e6a73daf6662a1c64\" width=\"\"><\/div>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0420\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0437\u0430\u0434\u0443\u043c\u0430\u043d\u043e!<\/p>\n<h2>\u041f\u0440\u043e\u0434\u043e\u043b\u0436\u0430\u0435\u043c.<\/h2>\n<p>\u041d\u0430\u043f\u043e\u043b\u043d\u044f\u0435\u043c \u0432\u044c\u044e\u0448\u043a\u0443 \u0431\u043e\u0435\u0432\u044b\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 scrollContentBody<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">LazyVStack(spacing: 0) {     EntryScrollHeader()      ZStack {         VStack(spacing: 0) {             hugeSeparator             separator             hugeSeparator             RateView(rate: $rate, swipeEnabled: true)             hugeSeparator             EntryItemPairingView()             EntryAboutView()         }     }     VStack(spacing: 0) {         EntryNotesView()         EntryMapView()         EntryBestItemsView()         EntryLatestCommentsView()     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c<\/summary>\n<div class=\"spoiler__content\">\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/61ab73f46ea11f3bbbaf2087\" data-style=\"\" id=\"61ab73f46ea11f3bbbaf2087\" width=\"\"><\/div>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0412\u0440\u043e\u0434\u0435 \u0432\u0441\u0451 \u043d\u043e\u0440\u043c, \u043d\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e, \u0447\u0442\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044e \u043f\u043e\u043f\u043b\u043e\u0445\u0435\u043b\u043e &#8212; \u0441\u043a\u0440\u043e\u043b\u043b \u043b\u0430\u0433\u0430\u0435\u0442. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u044c\u044e\u0448\u043a\u0430 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u043f\u0435\u0440\u0435\u0433\u0440\u0443\u0436\u0435\u043d\u0430 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u043e\u043c, \u043e\u0434\u043d\u0430\u043a\u043e \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0430\u044f \u0432 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u043c UIKit, \u043d\u0435 \u0442\u043e\u0440\u043c\u043e\u0437\u0438\u043b\u0430.<\/p>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u0442\u0438\u043b \u043f\u0440\u043e\u0444\u0438\u043b\u0438\u0440\u043e\u0432\u0449\u0438\u043a SwiftUI<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/webt\/3m\/u6\/pk\/3mu6pkkdweeo21fxkc8ge8ymvho.png\" alt=\"\" title=\"\" width=\"875\" height=\"251\" data-src=\"https:\/\/habrastorage.org\/webt\/3m\/u6\/pk\/3mu6pkkdweeo21fxkc8ge8ymvho.png\"\/><figcaption><\/figcaption><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u0412 \u043f\u0440\u043e\u0444\u0438\u043b\u0438\u0440\u043e\u0432\u0449\u0438\u043a\u0435 \u044f \u0443\u0437\u043d\u0430\u043b, \u0447\u0442\u043e \u043c\u043e\u0438 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u043e\u0447\u0435\u043d\u044c \u0434\u043e\u043b\u0433\u043e \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u044e\u0442\u0441\u044f. \u041d\u043e \u0437\u0430\u0447\u0435\u043c \u0438\u0445 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u0442\u044c \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e, \u0432\u0435\u0434\u044c \u043e\u043d\u0438 \u043d\u0435 \u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f? \u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433 \u0432 SwiftUI \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043f\u0440\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 @State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0445. \u0410 \u0432 \u043d\u0435\u0451 \u043a\u0430\u043a \u0440\u0430\u0437 \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u0435 ScrollView. \u0422\u043e \u0435\u0441\u0442\u044c \u0440\u0435\u043d\u0434\u0435\u0440 \u0443 \u043c\u0435\u043d\u044f \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u044f \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u044e \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u043f\u0430\u043b\u044c\u0446\u0435\u043c \u043f\u043e \u044d\u043a\u0440\u0430\u043d\u0443!<\/p>\n<p>\u041d\u0435 \u043e\u0447\u0435\u043d\u044c \u0432\u0435\u0441\u0435\u043b\u043e. \u041f\u043e\u0448\u0451\u043b \u0432 \u0433\u0443\u0433\u043b \u0438\u0441\u043a\u0430\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u044f, \u043d\u043e \u0431\u043e\u043b\u044c\u0448\u0438\u043d\u0441\u0442\u0432\u043e \u0438\u0437 \u043d\u0438\u0445 \u0431\u044b\u043b\u0438 \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b \u0438 \u043f\u043e\u0445\u043e\u0436\u0438 \u043d\u0430 \u043c\u043e\u0451. <\/p>\n<p>\u041d\u0430\u0442\u043a\u043d\u0443\u043b\u0441\u044f \u043d\u0430 <a href=\"https:\/\/zacwhite.com\/2019\/scrollview-content-offsets-swiftui\/\" rel=\"noopener noreferrer nofollow\">\u0441\u0442\u0430\u0442\u044c\u044e<\/a>, \u0433\u0434\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u043d\u0430\u0441\u043b\u0435\u0434\u043d\u0438\u043a\u0430 ScrollView, \u043d\u043e \u043f\u0440\u0438 \u043f\u043e\u043f\u044b\u0442\u043a\u0435 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u0442\u044c \u0442\u0443 \u043c\u0430\u0433\u0438\u044e \u044f \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u043e\u0439 \u0436\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043e\u0439 &#8212; \u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0442\u0441\u044f \u043f\u043b\u0430\u0432\u043d\u043e, \u0430 \u0442\u043e \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435 &#8212; \u0443\u0436\u0435 \u0441 \u0442\u043e\u0440\u043c\u043e\u0437\u0430\u043c\u0438.<\/p>\n<h2>\u041a\u0430\u043a \u044f \u0440\u0435\u0448\u0438\u043b \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443.<\/h2>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0442\u043e\u0433\u043e \u043a\u0430\u043a \u044f \u043f\u0435\u0440\u0435\u0431\u0440\u0430\u043b \u043a\u0443\u0447\u0443 \u0441\u0442\u0430\u0442\u0435\u0439, \u043f\u043e\u0433\u043e\u0440\u0435\u0432\u0430\u043b \u043e \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u0432\u043f\u0443\u0441\u0442\u0443\u044e \u043f\u043e\u0442\u0440\u0430\u0447\u0435\u043d\u043d\u043e\u043c \u0432\u0440\u0435\u043c\u0435\u043d\u0438 (\u0431\u044b\u043b \u0448\u0430\u043d\u0441 \u043e\u0441\u0442\u0430\u0442\u044c\u0441\u044f \u0431\u0435\u0437 \u0432\u043e\u0437\u043d\u0430\u0433\u0440\u0430\u0436\u0434\u0435\u043d\u0438\u044f &#8212; \u043a\u043e\u043c\u0443 \u043d\u0443\u0436\u043d\u043e \u0442\u043e\u0440\u043c\u043e\u0437\u044f\u0449\u0435\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435?), \u043c\u043d\u0435 \u0432 \u0433\u043e\u043b\u043e\u0432\u0443 \u043f\u0440\u0438\u0448\u043b\u0430 \u0438\u0434\u0435\u044f. \u0417\u0430\u0447\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c @State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435, \u0435\u0441\u043b\u0438 \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c?<\/p>\n<p>\u0421\u0443\u0442\u044c \u0438\u0434\u0435\u0438 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u0435 ScrollView \u043e\u0442\u043b\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 GeometryReader. \u041d\u043e \u0432\u0435\u0434\u044c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0438\u0437 \u043d\u0435\u0433\u043e \u043c\u043e\u0436\u043d\u043e \u043d\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043d\u0430\u0440\u0443\u0436\u0443, \u0440\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0439 \u0432\u044c\u044e\u0448\u043a\u0435, \u0430 \u0434\u0430\u0432\u0430\u0442\u044c \u0434\u043e\u0447\u0435\u0440\u043d\u0438\u043c \u0432\u044c\u044e\u0448\u043a\u0430\u043c!<\/p>\n<p>\u0418\u0442\u0430\u043a, \u043f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c:<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u043c \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u0441\u043c\u0435\u0449\u0430\u0442\u044c \u0441 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e, \u0432\u043d\u0443\u0442\u0440\u044c GeometryReader<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">ScrollView {     LazyVStack {         Color.clear             .frame(width: size.width, height: size.height - 40)         ZStack(alignment: .top) {             GeometryReader { proxy in                 let offset = proxy.frame(in: .named(\"scroll\")).minY                 Text(\"\\(offset)\")                 topBackgroundBody(offset: offset)                 EntryInfoView(imageOffset: topImageOffset(scrollOffset: offset))             }              scrollContentBody         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0422\u0430\u043a\u0436\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0435\u0440\u0435\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u0434\u0441\u0447\u0451\u0442 \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u043d\u043e\u0433\u043e \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u044f<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">private func topBackgroundOffset(scrollOffset: CGFloat) -> CGFloat {     min(-scrollOffset, -scrollOffset * 0.9 - topBarHeight) }  private func topImageOffset(scrollOffset: CGFloat) -> CGFloat {     let bounds = UIScreen.main.bounds     return -scrollOffset * 0.8 - bounds.height \/ 20 }<\/code><\/pre>\n<p>\u0412\u0435\u0434\u044c \u0440\u0430\u043d\u044c\u0448\u0435 \u043e\u0442\u0441\u0442\u0443\u043f \u0431\u044b\u043b \u043e\u0442 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0439 \u0432\u044c\u044e\u0448\u043a\u0438, \u0430 \u0442\u0435\u043f\u0435\u0440\u044c \u043e\u0442 \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430 ScrollView.<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u043f\u0443\u0441\u043a\u0430\u0435\u043c <\/summary>\n<div class=\"spoiler__content\">\n<div class=\"tm-iframe_temp\" data-src=\"https:\/\/embedd.srv.habr.com\/iframe\/61ab7b766a73daf6662a1c65\" data-style=\"\" id=\"61ab7b766a73daf6662a1c65\" width=\"\"><\/div>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041d\u0443 \u0432\u043e\u0442, \u0442\u0435\u043f\u0435\u0440\u044c \u0441\u0442\u0430\u043b\u043e \u043d\u0430\u043c\u043d\u043e\u0433\u043e \u043f\u043b\u0430\u0432\u043d\u0435\u0435.<\/p>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0451 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043a\u043e\u043c\u0443-\u043d\u0438\u0431\u0443\u0434\u044c \u0435\u0449\u0451, \u0430 \u0442\u0430\u043a\u0436\u0435 \u0447\u0442\u043e \u043e\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0443\u043b\u0443\u0447\u0448\u0435\u0433\u043e \u0438 \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043e.<\/p>\n<p>\u042f \u0432\u044b\u043b\u043e\u0436\u0438\u043b \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u043d\u0430 \u0432\u0441\u0435\u043e\u0431\u0449\u0435\u0435 \u043e\u0431\u043e\u0437\u0440\u0435\u043d\u0438\u0435 \u0432 <a href=\"https:\/\/github.com\/gf59ru\/Article.SwiftUIScrollView\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0439<\/a> \u0438 \u0440\u0430\u0437\u0431\u0438\u043b \u044d\u0442\u0430\u043f\u044b \u043f\u043e \u0432\u0435\u0442\u043a\u0430\u043c. \u041d\u0430\u0447\u0430\u043b\u043e \u0432 \u0432\u0435\u0442\u043a\u0435 <a href=\"https:\/\/github.com\/gf59ru\/Article.SwiftUIScrollView\/tree\/start\" rel=\"noopener noreferrer nofollow\">start<\/a>, \u0441\u043a\u0440\u043e\u043b\u043b \u0441 \u0442\u043e\u0440\u043c\u043e\u0437\u0430\u043c\u0438 &#8212; \u0432 \u0432\u0435\u0442\u043a\u0435 <a href=\"https:\/\/github.com\/gf59ru\/Article.SwiftUIScrollView\/tree\/freezing_scroll\" rel=\"noopener noreferrer nofollow\">freezing_scroll<\/a>, \u043f\u043b\u0430\u0432\u043d\u044b\u0439 \u0441\u043a\u0440\u043e\u043b\u043b &#8212; \u0432 \u0432\u0435\u0442\u043a\u0435 <a href=\"https:\/\/github.com\/gf59ru\/Article.SwiftUIScrollView\/tree\/smooth_scroll\" rel=\"noopener noreferrer nofollow\">smooth_scroll<\/a>. \u0421\u043c\u043e\u0442\u0440\u0438\u0442\u0435, \u043f\u043e\u043b\u044c\u0437\u0443\u0439\u0442\u0435\u0441\u044c, \u043a\u0440\u0438\u0442\u0438\u043a\u0443\u0439\u0442\u0435, \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u0439\u0442\u0435!<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/589051\/\"> https:\/\/habr.com\/ru\/post\/589051\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u0412\u0441\u0435\u043c \u043f\u0440\u0438\u0432\u0435\u0442! \u041c\u0435\u043d\u044f \u0437\u043e\u0432\u0443\u0442 \u041d\u0438\u043a\u043e\u043b\u0430\u0439, \u044f iOS-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434\u043e \u043c\u043d\u043e\u0439 \u0432\u043e\u0437\u043d\u0438\u043a\u043b\u0430 \u0437\u0430\u0434\u0430\u0447\u0430 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u043d\u0430 \u0437\u0430\u0434\u043d\u0435\u043c \u043f\u043b\u0430\u043d\u0435 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043f\u0440\u043e\u043a\u0440\u0443\u0447\u0438\u0432\u0430\u0435\u0442\u0441\u044f \u0434\u0440\u0443\u0433\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442. \u041f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0431\u044b\u0442\u044c \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0439, \u043d\u043e \u0441 \u0437\u0430\u043c\u0435\u0434\u043b\u0435\u043d\u043d\u043e\u0439 \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c\u044e &#8212; \u043a\u0430\u043a \u043e\u0442\u0434\u0430\u043b\u0451\u043d\u043d\u044b\u0439 \u0444\u043e\u043d \u0432 \u043c\u0443\u043b\u044c\u0442\u0444\u0438\u043b\u044c\u043c\u0430\u0445 \u0438\u043b\u0438 \u0438\u0433\u0440\u0430\u0445.<\/p>\n<h2>\u0418\u0442\u0430\u043a, \u043d\u0430\u0447\u0438\u043d\u0430\u0435\u043c.<\/h2>\n<p>\u0412 \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u043e\u043c UIScrollView \u0438\u0437 UIKit \u043c\u043e\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollviewdelegate\" rel=\"noopener noreferrer nofollow\">UIScrollViewDelegate<\/a> &#8212; \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/developer.apple.com\/documentation\/uikit\/uiscrollviewdelegate\/1619392-scrollviewdidscroll\" rel=\"noopener noreferrer nofollow\">scrollViewDidScroll(_ scrollView: UIScrollView)<\/a> \u0441\u043a\u0430\u0436\u0435\u0442 \u043d\u0430\u043c, \u043d\u0430\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043c\u0435\u0441\u0442\u0438\u043b\u0441\u044f \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u043a\u043e\u043d\u0442\u0435\u043d\u0442. \u041d\u043e \u0432 SwiftUI ScrollView \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0434\u0435\u043b\u0435\u0433\u0430\u0442\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043b\u043e\u0432\u0438\u0442\u044c \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043f\u043e\u0441\u043e\u0431\u0430\u043c\u0438.<\/p>\n<p>\u042f \u043d\u0430\u0448\u0451\u043b \u0441\u043f\u043e\u0441\u043e\u0431 \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0442\u044c \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u0435 &#8212; <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/geometryreader\" rel=\"noopener noreferrer nofollow\">GeometryReader<\/a> \u0432\u043d\u0443\u0442\u0440\u0438 ScrollView:<\/p>\n<pre><code class=\"swift\">struct ContentView: View {     @State private var scrollOffset = CGFloat(0)      var body: some View {         ScrollView {             ZStack {                 Color.green                     .opacity(0.5)                     .frame(height: UIScreen.main.bounds.height * 3)                  GeometryReader { proxy in                     let offset = proxy.frame(in: .named(\"scroll\")).minY                     scrollOffset = offset                 }             }         }     } }<\/code><\/pre>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e GeometryReader \u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0438\u0441\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u0432 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435. \u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043d\u0430 \u0442\u0430\u043a\u043e\u0439 \u043a\u043e\u0434 \u0440\u0443\u0433\u0430\u0435\u0442\u0441\u044f<\/p>\n<pre><code>\"Type '()' cannot conform to 'View'<\/code><\/pre>\n<p> \u0414\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e, \u043f\u043e\u0442\u0440\u0435\u0431\u043e\u0432\u0430\u043b\u043e\u0441\u044c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/developer.apple.com\/documentation\/swiftui\/preferencekey\" rel=\"noopener noreferrer nofollow\">PreferenceKey<\/a>:<\/p>\n<pre><code class=\"swift\">struct ScrollOffsetPreferenceKey: PreferenceKey {     static var defaultValue: CGFloat = 0      static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {         value = nextValue()     } }<\/code><\/pre>\n<p>\u041c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 GeometryReader:<\/p>\n<pre><code class=\"swift\">GeometryReader { proxy in     let offset = proxy.frame(in: .named(\"scroll\")).minY     Color.clear.preference(key: ScrollOffsetPreferenceKey.self,                            value: offset) }<\/code><\/pre>\n<p>\u042d\u0442\u043e \u0432\u044b\u043d\u043e\u0441 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u044f \u0432 PreferenceKey. \u041a\u0440\u043e\u043c\u0435 \u044d\u0442\u043e\u0433\u043e, \u043d\u0430\u0434\u043e \u0435\u0449\u0451 \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 State-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e:<\/p>\n<pre><code class=\"swift\">.onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in     scrollOffset = value }<\/code><\/pre>\n<p>\u041a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043d\u0435 \u0440\u0443\u0433\u0430\u0435\u0442\u0441\u044f. \u0422\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0434\u043e \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u043e\u0441\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u044c. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0434\u0435\u043b\u0430\u0435\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435:<\/p>\n<ul>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043a\u043e\u043d\u0442\u0435\u043d\u0442, \u0447\u0442\u043e\u0431\u044b \u0431\u044b\u043b\u043e \u0447\u0442\u043e \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0442\u044c, \u0438 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e. \u0421\u0440\u0430\u0437\u0443 \u0432\u044b\u043d\u043e\u0441\u0438\u043c \u0435\u0433\u043e \u0438\u0437 <code>body:<\/code><\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"swift\">var body: some View {     VStack {     ScrollView {         ZStack {             VStack {                 scrollViewContentBody             }             .opacity(0.75)                        GeometryReader { proxy in                 let offset = proxy.frame(in: .named(\"scroll\")).minY                 Color.clear.preference(key: ScrollOffsetPreferenceKey.self,                                        value: offset)             }         }     }     .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in         scrollOffset = value     } }  @ViewBuilder private var scrollViewContentBody: some View {     Text(\"Lorem ipsum\")         .font(.largeTitle)         .padding(16)      separator      Text(\"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\")         .font(.title)         .padding(16)      separator      Text(\"At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident, similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat.\")         .font(.title)         .padding(16)     separator }  private var separator: some View {     Color.gray         .frame(height: 1 \/ UIScreen.main.scale)         .padding(16) }<\/code><\/pre>\n<ul>\n<li>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0434\u0435\u0431\u0430\u0436\u043d\u044b\u0439 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f<\/p>\n<\/li>\n<\/ul>\n<pre><code class=\"swift\">var body: some View {     VStack {         Text(\"\\(scrollOffset)\") \/\/ &lt;-- \u044d\u0442\u043e\u0442 \u044d\u043b\u0435\u043c\u0435\u043d\u0442 \u043f\u043e\u043a\u0430\u0436\u0435\u0442 \u043d\u0430\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043c\u0435\u0449\u0435\u043d\u0438\u044f         ScrollView {             \/\/ ...<\/code><\/pre>\n<details class=\"spoiler\">\n<summary>\u0421\u043c\u043e\u0442\u0440\u0438\u043c \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c<\/summary>\n<div class=\"spoiler__content\">\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<\/p>\n<\/div>\n<\/details>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0442\u0441\u044f &#8212; \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043b\u043e\u0432\u0438\u0442\u0441\u044f. \u041c\u0435\u043d\u044f\u0435\u043c \u043f\u0440\u043e\u0431\u043d\u0438\u043a\u0438 \u043d\u0430 \u0440\u0430\u0431\u043e\u0447\u0438\u0439 \u043a\u043e\u0434.<\/p>\n<details class=\"spoiler\">\n<summary>\u0413\u043b\u0430\u0432\u043d\u0430\u044f \u0432\u044c\u044e\u0448\u043a\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">struct ScrollOffsetPreferenceKey: PreferenceKey {     static var defaultValue: CGFloat = 0      static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {         value = nextValue()     } }  struct RootView: View {         @State private var scrollOffset = CGFloat(0)     @State private var rate: Decimal? = 3.8          \/\/ MARK: - Vars          private var scrolledEnoughToShowTopBar: Bool {         -scrollOffset > -90     }          private var topBarHeight: CGFloat {         UIApplication.shared.safeAreaInsets.top + 42     }      private var topBackgroundOffset: CGFloat {         let bounds = UIScreen.main.bounds         return scrollOffset \/ 10 - bounds.height \/ 8     }      private var topImageOffset: CGFloat {         let bounds = UIScreen.main.bounds         return scrollOffset \/ 5 - bounds.height \/ 20     }      \/\/ MARK: - UI      @ViewBuilder     var body: some View {         let screenBounds = UIScreen.main.bounds         ZStack(alignment: .top) {             AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/8856514\/pexels-photo-8856514.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))                 .blur(radius: 4)                 .frame(width: screenBounds.width, height: screenBounds.height)                 .aspectRatio(contentMode: .fill)                 .clipped()              Group {                 topBackgroundBody                 EntryInfoView(imageOffset: topImageOffset)                 scrollBody.padding(.top, -200)             }         }         .ignoresSafeArea(.all, edges: [.top, .bottom])     }      private var topBackgroundBody: some View {         AnimatedImage(url: URL(string: \"https:\/\/images.pexels.com\/photos\/10288317\/pexels-photo-10288317.jpeg?auto=compress&amp;cs=tinysrgb&amp;dpr=1&amp;w=500\"))             .resizable()             .aspectRatio(contentMode: .fill)             .frame(width: UIScreen.main.bounds.width,                    height: UIScreen.main.bounds.height \/ 1.5)             .aspectRatio(contentMode: .fill)             .clipped()             .offset(y: topBackgroundOffset)     }      @ViewBuilder     private var scrollBody: some View {         let size = UIScreen.main.bounds         ScrollView {             LazyVStack {                 Color.clear                     .frame(width: size.width, height: size.height - 40)                 ZStack(alignment: .top) {                     scrollContentBody                      GeometryReader { proxy in                         let offset = proxy.frame(in: .named(\"scroll\")).minY                         Text(\"\\(offset)\")                         Color.clear.preference(key: ScrollOffsetPreferenceKey.self, value: offset)                     }                 }                 Color.clear                     .frame(width: size.width,                            height: UIApplication.shared.safeAreaInsets.bottom)             }         }         .onPreferenceChange(ScrollOffsetPreferenceKey.self) { value in             scrollOffset = value         }     }          private var scrollContentBody: some View {         LazyVStack(spacing: 0) {             EntryScrollHeader()              Color.green.opacity(0.5)                 .frame(height: 200)              Color.red.opacity(0.5)             .frame(height: 1000)         }     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0417\u0430\u0433\u043e\u043b\u043e\u0432\u043e\u043a \u0441\u043a\u0440\u043e\u043b\u043b\u0438\u0440\u0443\u0435\u043c\u043e\u0433\u043e \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">struct EntryScrollHeader: View {     var body: some View {         VStack(spacing: 0) {             HStack {                 VStack {                     Text(\"Description\")                 }                  Spacer()                  Button {                  } label: {                     Image(systemSymbol: .bookmark)                         .foregroundColor(.black.opacity(0.25))                         .font(.system(size: 25, weight: .regular, design: .default))                 }             }             .padding(.bottom, 4)              HStack {                 VStack {                     Text(\"Lorem\")                     Text(\"Ipsum\")                 }                 Spacer()             }          }         .frame(height: 76)         .padding([.leading, .trailing], 15)         .padding(.top, 12)         .background(Color.white)     } }<\/code><\/pre>\n<\/p>\n<\/div>\n<\/details>\n<details class=\"spoiler\">\n<summary>\u0418 \u0432\u0435\u0440\u0445\u043d\u044f\u044f \u0432\u044c\u044e\u0448\u043a\u0430<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"swift\">struct EntryInfoView: View {     private var imageOffset: CGFloat      init(imageOffset: CGFloat) {         self.imageOffset = imageOffset     }      @ViewBuilder     var body: some View {         let screenBounds = UIScreen.main.bounds          ZStack(alignment: .topLeading) {             HStack {                 infoBody                  cardsBody                     .frame(height: screenBounds.height \/ 4)                     .padding(8)             }             .padding(16)         }         .offset(y: imageOffset)     }      @ViewBuilder     private var infoBody: some View {         let screenBounds = UIScreen.main.bounds         let rating = Decimal(4.2)          VStack(spacing: 0) {             Text(rating.ratingString)                 .font(.system(size: 24))                 .padding(.top, 8)              Text(\"49,849\")                 .font(.system(size: 10))                 .padding(.top, 4)              EntryLittleStarsView(rating: rating)                 .foregroundColor(.orange)                 .padding(4)         }         .frame(minHeight: screenBounds.width \/ <\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-343516","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/343516","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=343516"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/343516\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=343516"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=343516"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=343516"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}