9

How can we set right to left layout direction in Horizontal ScrollView in swiftUI?

something like this:

ScrollView(showsHorizontalIndicator: false) {
    HStack {
        VStack {
            Image("1")
            Text("one")
        }
        VStack {
            Image("2")
            Text("two")
        }
        VStack {
            Image("3")
            Text("three")
        }
    }
}

so when simulator run, I want to see Image("1") in right hand side and swipe to right to can access to Image("3")

Sajjad
  • 1,536
  • 2
  • 17
  • 31
  • You should probably update your question to make it more clear. I would rephrase it: "I have a ScrollView setup with .rigthToLeft, but when the view is created, it appears scrolled to the left, instead of the right.". Or something of the like. – kontiki Jul 08 '19 at 19:52
  • my Question is something like this but in SwiftUI! https://stackoverflow.com/questions/9140959/uiscrollview-direction-rtl-for-right-to-left-languages – Sajjad Jul 08 '19 at 19:57
  • when I use `transformEffect(.init(scaleX: -1, y: 1))` in `SwiftUI`, contents in scrollview disappear – Sajjad Jul 08 '19 at 19:58
  • The transformEffect is doing it's job, but the view is shifted out of the screen. If you use .offset() you can bring it back. However, now the problem is that the images and the text are shown as if they were reflected on a mirror! – kontiki Jul 08 '19 at 20:31

3 Answers3

11

You can make any view you want right-to-left by giving your view below modifiers:

.flipsForRightToLeftLayoutDirection(true)
.environment(\.layoutDirection, .rightToLeft)

In your case, it is going to be like this:

ScrollView(showsHorizontalIndicator: false) {
    HStack {
        VStack {
            Image("1")
            Text("one")
        }
        VStack {
            Image("2")
            Text("two")
        }
        VStack {
            Image("3")
            Text("three")
        }
    }
}
.flipsForRightToLeftLayoutDirection(true)
.environment(\.layoutDirection, .rightToLeft)

GoodLuck dadash ;)

Arun Reddy
  • 3,443
  • 1
  • 21
  • 16
FRIDDAY
  • 3,781
  • 1
  • 29
  • 43
  • 6
    Thanks! But when I use Persian(rtl language) Characters inside Text, my text has mirrored! – Moeen Kashisaz Dec 09 '20 at 06:43
  • turn '''.flipsForRightToLeftLayoutDirection(true)''' to false.@MoeenKashisaz – FRIDDAY Dec 09 '20 at 08:51
  • 2
    Clarifying the above for anyone else: .environment(.layoutDirection, .rightToLeft) *forces* a right to left layout to happen, even in L2R locales. The flip modifier makes the view obey the environment. – Luke Smith Jul 07 '21 at 17:08
0

You can also use the my helper.

import SwiftUI

struct ScrollViewRTL<Content: View>: View {
  @ViewBuilder var content: Content
  @Environment(\.layoutDirection) private var layoutDirection
  var type: RowType
   
  init(type: RowType, @ViewBuilder content: () -> Content) {
    self.type = type
    self.content = content()
  }
   
  @ViewBuilder var body: some View {
    ScrollView(type.scrollAxis, showsIndicators: false) {
      content
        .rotation3DEffect(Angle(degrees: layoutDirection == .rightToLeft ? -180 : 0), axis: (
          x: CGFloat(0),
          y: CGFloat(layoutDirection == .rightToLeft ? -10 : 0),
          z: CGFloat(0)))
       
    }
    .rotation3DEffect(Angle(degrees: layoutDirection == .rightToLeft ? 180 : 0), axis: (
      x: CGFloat(0),
      y: CGFloat(layoutDirection == .rightToLeft ? 10 : 0),
      z: CGFloat(0)))
  }
}

public enum RowType {
  case hList
  case vList
   
  var scrollAxis: Axis.Set {
    switch self {
    case .hList:
      return .horizontal
       
    case .vList:
      return .vertical
    }
  }
}
Metin Atalay
  • 1,375
  • 18
  • 28
0
import SwiftUI
import Introspect

struct HorizontalScrollView<Content: View>: View {
    
    private let content: Content
    private let spacing: CGFloat
    private let showsIndicators: Bool
    
    init(showsIndicators: Bool = false, spacing: CGFloat = 8, @ViewBuilder content: () -> Content) {
        self.spacing = spacing
        self.showsIndicators = showsIndicators
        self.content = content()
    }
    
    var body: some View {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack(alignment: .center, spacing: spacing) {
                if UIDevice.isAvailable16_4 {
                    content
                } else {
                    content
                        .rotation3DEffect(Angle(degrees: 180), axis: (x: CGFloat(0), y: CGFloat(10), z: CGFloat(0)))
                }
            }
            .padding([.trailing, .leading], .len16)
        }
        .introspectScrollView { scrollView in
            scrollView.alwaysBounceHorizontal = false
        }
        .flipsForRightToLeftLayoutDirection(!UIDevice.isAvailable16_4)
        .environment(\.layoutDirection, .rightToLeft)
    }
    
}

extension UIDevice {
    
    static var isAvailable16_4: Bool {
        if #available(iOS 16.4, *) {
            return true
        }
        return false
    }
    
}

Use:

HorizontalScrollView {
    ForEach(viewModel.categories) { category in
        StoryCategoryItemView(viewModel: .init(category, categories: viewModel.categories))
    }
}
Mohammad Razipour
  • 3,643
  • 3
  • 29
  • 49