How to only disable scroll in ScrollView but not content view?
Only pass .horizontal
as the scroll axis if you want the view to scroll. Otherwise, pass the empty set.
struct TestView: View {
@Binding var shouldScroll: Bool
var body: some View {
ScrollView(axes, showsIndicators: false) {
Text("Your content here")
}
}
private var axes: Axis.Set {
return shouldScroll ? .horizontal : []
}
}
Disable the scrolling but not the content (iOS 13 and higher)
Don't forget to provide a minimum reproducible example something we can copy and paste to ensure that the integrity if what you want is kept.
If your *StepView.count is static the code below might work for you
struct ButtonScroll: View {
@State var myId: Int = 0
init() {
//Add this
//It does NOT work with ScrollView
UIScrollView.appearance().isScrollEnabled = false
}
var body: some View {
VStack{
//Simulates myId changing
Button("change-position", action: {
myId = Int.random(in: 0...3)
print(myId.description)
})
ZStack{
//TabView does not allow for changes in Tab/Page count nicely
TabView(selection: $myId){
//Used Button to test interaction
Button("FirstStepView", action: {
print("FirstStepView")
}).tag(0)
Button("SecondStepView", action: {
print("SecondStepView")
}).tag(1)
Button("ThirdStepView", action: {
print("ThirdStepView")
}).tag(2)
Button("FourthStepView", action: {
print("FourthStepView")
}).tag(3)
}.tabViewStyle(PageTabViewStyle(indexDisplayMode: .never))
.animation(
Animation.easeOut(duration: 1)
)
}
}
}
}
How i can disable Scroll on Content inside of two scrollview
i have found an answer, we can handle it with ref , check for demo link
https://snack.expo.dev/@mirza009/table
SwfitUI List Make Scrolling disabled
Instead of using List
which is scrollable, use ForEach
.
ForEach
is not scrollable by default.
Here is an example using ForEach
struct ContentView: View {
let colors: [Color] = [.red, .green, .blue]
var body: some View {
VStack {
ForEach(colors, id: \.self) { color in
Text(color.description.capitalized)
.padding()
.background(color)
}
}
}
}
A good resource for ForEach
can be found here similarly one for List
can be found here.
Update
There are currently two ways to stop a List
from scrolling:
- Using
UITableView.appearance().isScrollEnabled = false
(but this can unintended side-effects) - Using the 3rd party dependency Introspect
Caveats
List
contained in a NavigationView
If your List
is contained in a NavigationView
then it will still scroll. Neither of the above methods stop it from scrolling.
Here is an example view:
import SwiftUI
import Introspect
struct ContentView: View {
var body: some View {
NavigationView {
List {
ForEach(0..<10, id: \.self) { _ in
NavigationLink("Tap") {
Text("Hello").onTapGesture {
print("hello")
}
}
}
}
// .onAppear {
// UIScrollView.appearance().isScrollEnabled = false
// }
// .introspectTableView { tableView in
// tableView.isScrollEnabled = false
// }
}
}
}
Uncommenting the onAppear
or the introspectTableView
does not stop the List
from scrolling.
List
not contained in a NavigationView
If the List
is not contained in a NavigationView
then we can be possible to stop the List
from scrolling.
If we remove the NavigationView
from the above example, we can see that by uncommenting the onAppear
or the introspectTableView
the List
does stop scrolling.
Here is a video of the above situations.
Disable vertical scroll in UIScrollView Swift
Since iOS 11 content insets can get adjusted automatically. Try setting your scrollview's behaviour to not adjust it.
if #available(iOS 11, *) {
scrollview.contentInsetAdjustmentBehavior = .never
}
Disable ScrollView Programmatically?
Several points to begin with:
- You cannot disable the scrolling of a ScrollView. You would need to extend to ScrollView and override the
onTouchEvent
method to returnfalse
when some condition is matched. - The Gallery component scrolls horizontally regardless of whether it is in a ScrollView or not - a ScrollView provides only vertical scrolling (you need a HorizontalScrollView for horizontal scrolling)
- You seem to say you have a problem with the image stretching itself -- this has nothing to do with the ScrollView, you can change how an ImageView scales with the
android:scaleType
property (XML) or thesetScaleType
method - for instanceScaleType.CENTER
will not stretch your image and will center it at it's original size
You could modify ScrollView
as follows to disable scrolling
class LockableScrollView extends ScrollView {
...
// true if we can scroll (not locked)
// false if we cannot scroll (locked)
private boolean mScrollable = true;
public void setScrollingEnabled(boolean enabled) {
mScrollable = enabled;
}
public boolean isScrollable() {
return mScrollable;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
// if we can scroll pass the event to the superclass
return mScrollable && super.onTouchEvent(ev);
default:
return super.onTouchEvent(ev);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// Don't do anything with intercepted touch events if
// we are not scrollable
return mScrollable && super.onInterceptTouchEvent(ev);
}
}
You would then use
<com.mypackagename.LockableScrollView
android:id="@+id/QuranGalleryScrollView"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<Gallery android:id="@+id/Gallery"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="horizontal">
</Gallery>
</com.mypackagename.LockableScrollView>
in your XML file (just changed the ScrollView
to your special LockableScrollView
).
Then call
((LockableScrollView)findViewById(R.id.QuranGalleryScrollView)).setScrollingEnabled(false);
to disable scrolling of the view.
I think that you have more than just the issue of disabling scrolling though to achieve your desired result (the gallery will remain scrollable with the above code for instance) - I'd recommend doing some more research on each of the three components (Gallery, ScrollView, ImageView) to see what properties each one has and how it behaves.
Disable Scrolling of UIScrollView on part of the ScrollView.frame
Here is one way this can be achieved, instead of disabling a specific frame, you can disable interaction on over a specific view:
- SubClass the
UIView
you add on button tap with no real implementation but just for recognition - you can also use view tags if you prefer - SubClass the
UIScrollView
and implementtouchesShouldCancel
to cancel scrolling when interacting with a specific view - Set
scrollView.delaysContentTouches = false
Implementation
First I tried to recreate your situation, I created the child view controller class with a button which will go into the scrollview:
class ChildVC: UIViewController
{
let button = UIButton(type: .system)
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = .yellow
configureButton()
}
private func configureButton()
{
button.setTitle("Add View", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self,
action: #selector(didTapAddView),
for: .touchUpInside)
view.addSubview(button)
view.addConstraints([
button.leadingAnchor.constraint(equalTo: view.leadingAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor),
button.heightAnchor.constraint(equalToConstant: 50)
])
}
@objc
private func didTapAddView()
{
let customView = UIView()
customView.backgroundColor = .white
customView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(customView)
view.addConstraints([
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor,
constant: 16),
customView.topAnchor.constraint(equalTo: view.topAnchor,
constant: 16),
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor,
constant: -16),
customView.bottomAnchor.constraint(equalTo: button.topAnchor,
constant: -16)
])
}
}
Then I created your container view controller with the scroll view and embedded the child vc into the view controller:
class ScrollTestViewController: UIViewController
{
private let scrollView = UIScrollView()
private let childVC1 = ChildVC()
private let childVC2 = ChildVC()
private let childVCHeight: CGFloat = 250
private let childVCWidth: CGFloat = UIScreen.main.bounds.width
override func viewDidLoad()
{
super.viewDidLoad()
view.backgroundColor = .white
title = "Scroll Test"
configureScrollView()
configureChildVC1()
configureChildVC2()
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: childVC1.view.bounds.width * 2,
height: scrollView.bounds.height)
}
private func configureScrollView()
{
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
view.addConstraints([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
private func configureChildVC1()
{
addChild(childVC1)
childVC1.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(childVC1.view)
view.addConstraints([
childVC1.view.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
childVC1.view.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
childVC1.view.widthAnchor.constraint(equalToConstant: childVCWidth),
childVC1.view.heightAnchor.constraint(equalToConstant: childVCHeight)
])
}
private func configureChildVC2()
{
addChild(childVC2)
childVC2.view.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(childVC2.view)
view.addConstraints([
childVC2.view.leadingAnchor.constraint(equalTo: childVC1.view.trailingAnchor),
childVC2.view.centerYAnchor.constraint(equalTo: scrollView.centerYAnchor),
childVC2.view.widthAnchor.constraint(equalToConstant: childVCWidth),
childVC2.view.heightAnchor.constraint(equalToConstant: childVCHeight)
])
}
}
After doing this, you will get this result:
No different, user can scroll anywhere even when swiping on top of the newly added view.
So I made these changes:
- Create a custom view subclass so I can recognize which view I am dragging over
class CustomView: UIView
{
}
- Create a custom scrollview to disable interaction over specific views
fileprivate class CustomScrollView: UIScrollView
{
override func touchesShouldCancel(in view: UIView) -> Bool
{
// Just for demo, I added this
if view is CustomView
{
print("Scroll disabled, tapping custom view")
}
else
{
print("Scroll enabled")
}
// You only need this line
return !(view is CustomView)
}
}
- Make the changes in the container view controller with the scroll view
// Replace private let scrollView = UIScrollView() with
private let scrollView = CustomScrollView()
// Add this as well
scrollView.delaysContentTouches = false
- Make changes in the child view controllers to use Custom view when tapping the button
@objc
private func didTapAddView()
{
let customView = CustomView()
customView.backgroundColor = .white
customView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(customView)
view.addConstraints([
customView.leadingAnchor.constraint(equalTo: view.leadingAnchor,
constant: 16),
customView.topAnchor.constraint(equalTo: view.topAnchor,
constant: 16),
customView.trailingAnchor.constraint(equalTo: view.trailingAnchor,
constant: -16),
customView.bottomAnchor.constraint(equalTo: button.topAnchor,
constant: -16)
])
}
Now when you swipe over the view, it will prevent you from scrolling
Update based on Mihai's (OP's) comments
Do you have any idea how I could do it for a UIViewController instead
of UIView, Im asking because the view is currently UIViewController
and I lose some specifications about it in transforming it to an
UIView.So on the button press the view will add a UIViewController instead of
a UIView
A scrollview can add UIViews to it's view hierarchy therefore you can only check if the view you are scrolling on is a specific type of view.
However, I still think the solution can work if you want to add a view controller on a button tap instead of a UIView.
- Keep the custom view as it is:
fileprivate class CustomView: UIView
{
}
- Create a view controller subclass and change the default
view
to be the custom view subclass
fileprivate class PurpleVC: UIViewController
{
override func loadView()
{
super.loadView()
// Change the default view from a UIView
// to be of type CustomView
view = CustomView()
view.backgroundColor = .purple
}
}
- Add the new view controller on button tap instead of UIView
@objc
private func didTapAddView()
{
let customVC = PurpleVC()
customVC.view.translatesAutoresizingMaskIntoConstraints = false
addChild(customVC)
view.addSubview(customVC.view)
view.addConstraints([
customVC.view.leadingAnchor.constraint(equalTo: view.leadingAnchor,
constant: 16),
customVC.view.topAnchor.constraint(equalTo: view.topAnchor,
constant: 16),
customVC.view.trailingAnchor.constraint(equalTo: view.trailingAnchor,
constant: -16),
customVC.view.bottomAnchor.constraint(equalTo: button.topAnchor,
constant: -16)
])
}
- No change to custom scroll view as it should still detect the custom view which is the main view inside your view controller
fileprivate class CustomScrollView: UIScrollView
{
override func touchesShouldCancel(in view: UIView) -> Bool
{
// Just for demo, I added this
if view is CustomView
{
print("Scroll disabled, tapping custom view")
}
else
{
print("Scroll enabled")
}
// You only need this line
return !(view is CustomView)
}
}
Related Topics
Perform a Deeplink from Swiftui Widget on Tap
Detect App Launch from Widgetkit Widget Extension
Custom Back Indicator Image and iOS 11
Set Text Color and Font for Uidatepicker in iOS8/Swift
How to Change the Speed of Video Playback
Swift 3: Replace C Style For-Loop with Float Increment
Parsing JSON Within JSON in Objective-C
iOS Launch Screen in React Native
Xctest/Xctest.H Not Found on Old Projects Built in Xcode 6
Swift:Closure Declaration as Like Block Declaration
Searching Nsarray of Nsdictionary Objects
Invalid Signature (Invalid Binary) on Itunes Connect
Swift Optional Chaining Doesn't Work in Closure
Different Cornerradius for Each Corner Swift 3 - iOS
How to iOS Item and Font Sizes Scaling as Screen Size
Uilabel Text Truncation VS. Line Breaks in Text
Leaking Views When Changing Rootviewcontroller Inside Transitionwithview