How to entirely disable UITextView's scroll? - iphone

I'm developing a simple text editing app for iPad using UITextView. I always had some problems with UIScrollView and UITextView. I think I just expected too much things from these two.
When I set myTextView.text to another a NSString instance, scrolling automatically occurred. I could prevent this scrolling by setting
myTextView.scrollEnabled = NO;
myTextView.text = newText;
myTextView.scrollEnabled = YES;
However, if I changed the selectedRange property of myTextView, scrolling occurred.
Specifically, if selectedRange's range happened on text in the current screen, scrolling didn't occur.
For example, if I select all text by tapping "Select All" property, then scrolling didn't occur. But if I select all text by setting selectedRange to NSMakeRange(0, [myTextView.text length]), then scrolling to the END (the last caret position) occured.
To solve the problems,
1) I saved the original content offset of myTextView.
CGPoint originalOffset = myTextView.contentOffset;
// change myTextView.selectedRange here
myTextView.selectedRange = originalOffset
But nothing happend.
2) I called above codes after a few seconds using NSTimer, and scroll correctly returned to the original position(offset). However, scrolling to the end first occurred and then to the top..
Is there a way to entirely prevent UITextView's scrolling for a moment?

You can disable almost all scrolling by putting the following method into your UITextView subclass:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
// do nothing
}
The reason I say "almost" all scrolling is that even with the above, it still accepts user scrolls. Though you could disable those by setting self.scrollEnabled to NO.
If you want to only disable some scrolls, then make an ivar, lets call it acceptScrolls, to determine whether you want to allow scrolling or not. Then your scrollRectToVisible method can look like this:
- (void)scrollRectToVisible:(CGRect)rect animated:(BOOL)animated {
if (self.acceptScrolls)
[super scrollRectToVisible: rect animated: animated];
}

Related

UITextView doesn't show until it is scrolled

I am using a UITextView to display the text from a xml.I parsed the xml and stored the text in a NSArray and from there i am adding it to a UITextview in my code.
problem:
When i run the code the textview shows as empty or with partial text,and when i try to scroll the textview the whole text is getting displayed.
I added the UITextview to a UIView.
i found it strange and googled it but could not find many answers which helps me.
Can someone reply me to solve the issue
TNQ
Found a simple SOLUTION
Had the same problem. When you put text into a UITextView which is not visible, it doesn't draw the text.
I had my UITextView off screen and animated it in like this:
[UIView animateWithDuration:0.3 delay:0.0 options:UIViewAnimationCurveEaseOut animations:^{
self.contentView.center = CGPointMake(self.contentView.center.x + move * (self.contentView.frame.size.width /2), self.contentView.center.y);
self.textView.frame = self.textView.frame; // <<<--- ADD THIS LINE
} completion:^(BOOL finished) {
if (finished) {
}
}];
When you re-set the textview's frame, it re-draws it's text. So all you need to do is add the line I marked above before your UITextView comes on screen.
Cheers
UITextView inherits from UIScrollView, so just scroll it.
[detailsTextView setContentOffset:CGPointMake(0, 1) animated:YES];
i solved the issue by clearing the textview and added the text to the textview again,i dont know the exact funda but my issue is solved.
in my case:
i am changing the frame of the textview from outside to the view on click of a button.while the button is clicked the textview is not showing content and on scroll it shows.
i solved, by deleting the text from textview and re entering the text on clicking the button.
UItextView inherits from UIScrollView. That means it has scrolling functionality. Thus, if the contents of the UITextView is small enough to fit in its frame, it doesn't need to scroll.
But, if it is large enough, you need to scroll the UITextView.
Does this makes sense?
For more information, refer the class reference of UITextView
Ensure that, method in which u set textview's text is call properly.And string which u assign to text view is in proper string format(if not so retain it).
There are chances that the whitespaces and/or newlines are there before the actual text. Trim those characters before you assign it to text view.
NSCharacterSet *charset = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *text = [parsedString stringByTrimmingCharactersInSet:charset];
textview.text = text;
Please try it when your textView is loading
NSAttributedString *oldtext = objTextView.attributedText;
[objTextView removeFromSuperview];
objTextView.attributedText = oldtext;
[self.view addSubview:objTextView];
In my case my text is attributed
When you complete the operation which parses the XML are you ensuring that your update is occuring on the main thread ?
UIKit only updates on the main thread so any updates made off it will only show up next time you touch the object and force an update.
As an example of one way to do it correctly.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self parseXMLOffMainThread];
dispatch_async(dispatch_get_main_queue(), ^{
self.myTextView.text = parsedText;
});
});

Can I override scroll view's automatic behavior to scroll to the first responder?

I have a UITextField inside a UIScrollView (a few levels deep). I am watching UIKeyboardDidShowNotification, and also calling the same code when I manually change the first responder (I might change to a different text field without momentarily hiding the keyboard). In that code I use scrollRectToVisible:animated: to make sure the UITextField is visible.
I was having a huge headache debugging why that was acting funny, but I realized now that UIScrollView automatically ensures that the first responder is within its bounds. I am changing the frame of the UIScrollView so that none of it is hidden behind the keyboard.
However, my code can be slightly smarter than their code, because I want to show not only the UITextField, but some nearby related views as well. I try to show those views if they will fit; if not whatever, I try to show as much of them as I can but at least ensure that the UITextField is visible. So I want to keep my custom code.
The automatic behavior interferes with my code. What I see is the scroll view gently scroll up so that the bottom edge of my content is visible, then it snaps down to where my code told it to position.
Is there anyway to stop the UIScrollView from doing its default capability of scrolling the first responder into view?
More Info
On reviewing the documentation I read that they advise to change the scroll view's contentInset instead of frame. I changed that and eliminated some unpredictable behavior, but it didn't fix this particular problem.
I don't think posting all the code would necessarily be that useful. But here is the critical call and the values of important properties at that time. I will just write 4-tuples for CGRects; I mean (x, y, width, height).
[scrollView scrollRectToVisible:(116.2, 71.2, 60, 243) animated:YES];
scrollView.bounds == (0, 12, 320, 361)
scrollView.contentInset == UIEdgeInsetsMake(0, 0, 118, 0)
textField.frame == (112.2, 222.6, 24, 24)
converted to coordinates of the immediate subview of scrollView == (134.2, 244.6, 24, 24)
converted to coordinates of scrollView == (134.2, 244.6, 24, 24)
So the scroll view bottom edge is really at y == 243 because of the inset.
The requested rectangle extends to y == 314.2.
The text field extends to y == 268.6.
Both are out of bounds. scrollRectToVisible is trying to fix one of those problems. The standard UIScrollView / UITextField behavior is trying to fix the other. They don't come up with quite the same solution.
I didn't test this particular situation, but I've managed to prevent a scrollview from bouncing at the top and bottom by subclassing the scrollview and overriding setContentOffset: and setContentOffset:animated:. The scrollview calls this at every scroll movement, so I'm fairly certain they will be called when scrolling to the textfield.
You can use the delegate method textFieldDidBeginEditing: to determine when the scroll is allowed.
In code:
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
self.blockingTextViewScroll = YES;
}
-(void)setContentOffset:(CGPoint)contentOffset
{
if(self.blockingTextViewScroll)
{
self.blockingTextViewScroll = NO;
}
else
{
[super setContentOffset:contentOffset];
}
}
-(void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated
{
if(self.blockingTextViewScroll)
{
self.blockingTextViewScroll = NO;
}
else
{
[super setContentOffset:contentOffset animated:animated];
}
}
If your current scroll behaviour works with a setContentOffset: override, just place it inside the else blocks (or preferably, in a method you call from the else blocks).
In my project I have succeeded to achieve this by performing my scroll only after some delay.
- (void)keyboardWillShow:(NSNotification *)note
{
NSDictionary *userInfo = note.userInfo;
CGRect keyboardFrame = [[userInfo objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
UIEdgeInsets contentInsets = self.tableView.contentInset;
contentInsets.bottom += keyboardFrame.size.height;
[self.tableView setContentInset:contentInsets];
[self performSelector:#selector(scrollToEditableCell) withObject:nil afterDelay:0];
}
Also there is other possibility to make your view with additional views to be first responder and fool scroll view where to scroll. Haven't tested this yet.
This may turn out to be useless, but have you tried setting scrollView.userInteractionEnabled to NO before calling scrollrectToVisible: & then setting it back to YES? It may prevent the automatic scrolling behavior.
Try changing the view autoresizing to UIViewAutoresizingFlexibleBottomMargin | UIViewAutoresizingFlexibleRightMargin. The default is FlexibleTopMargin so maybe thats the reason. btw scrollRectToVisible: is using the scrollView.contentSize.
The other thing you can try to change the scrollView size first and then apply the scrollRectToVisible: change. First frame change, then content change. (Maybe observe the keyboard did appear event)
The automatic scrolling behavior seems to be especially buggy starting in iOS 14. I alleviated the problem by subclassing UIScrollView and overriding setContentOffset to do nothing. Here is the bases of my code.
class ManualScrollView: UIScrollView {
/// Use this function to set the content offset. This will forward the call to
/// super.setContentOffset(:animated:)
/// - Parameters:
/// - contentOffset: A point (expressed in points) that is offset from the content view’s origin.
/// - animated: true to animate the transition at a constant velocity to the new offset, false to make the transition immediate.
func forceContentOffset(_ contentOffset: CGPoint, animated: Bool) {
super.setContentOffset(contentOffset, animated: animated)
}
/// This function has be overriden to do nothing to block system calls from changing the
/// content offset at undesireable times.
///
/// Instead call forceContentOffset(:animated:)
override func setContentOffset(_ contentOffset: CGPoint, animated: Bool) {
}
}
This works but you have to deal with reimplementing many of the scroll views behaviors and methods that you normally get for free. Since scrollRectToView and scrollToView both use setContentOffset you also have to reimplement these if you want them to work.

Text shifted up in the textView while i focus on Textview second time

I use Textview for insert message text but when i focus in textview second time after insert some text then already inserted text shifted upside. so text cut in the TextView.
Any solution for this problem?
UITextView the subclass of UIScrollView. So you can discard scroll to top like this:
((UIScrollView*)someTextView).delegate = self; // or some other UIScrollViewDelegate
and implement this method:
-(void) scrollViewDidScroll:(UIScrollView*) scrollView {
scrollView.contentOffset = CGPointMake(0, 0);
}
But if you do need scrolling during edit, you can add some boolean flags to distinguish the situations when user is scrolling and when the scrolling is automatic.

Keyboard eats my Top Line of text in UITextView !

I met a really strange problem while using UITextView, I try to use "GrowStyle" of UITextView, wire up the frame with contentSize, turn off the .scrollEnabled, Build & Run: but when click the bottom of the screen, the keyboard showing up, the UITextView moves up a short distance, and its Top lines disappear, here is the code:
- (void)viewDidLoad {
[super viewDidLoad];
//Turn off the scrollEnabled.
UITextView *growStyleText.scrollEnabled = NO;
//Wire the growStyleText's contentSize to its frame, let it grow.
CGRect selfHack = growStyleText.frame;
selfHack.size = growStyleText.contentSize;
growStyleText.frame = selfHack;
//Make a UIScrollView
UIScrollView *scroll.contentSize = selfHack.size;
scroll.clipsToBounds = NO;
[self.view insertSubview:scroll atIndex:1];
//Add the TextView on the ScrollView, make it scrollable.
[scroll addSubview:growStyleText];
}
After click the last line, Keyboard shows up, then the Top line Disappears! I thought about for all day long, anybody seen this? how can I make Top line moves up instead of disappear? hard question for me, guess there may be simple answers, thank you, very much.
I don't know what you are trying to do, but, since UITextView is a subclass of UIScrollView, why do you need to create it as a subview of a UIScrollView?
Then, in my experience, you can't get the contentSize of a UIScrollView before you add it to a view.
I found this answer very useful
How do I size a UITextView to its content?
Hope it helps.

How to stop UITextView from scrolling up when entering it

I have a UITextView included in a UITableViewCell. The layout is correct when the views are initially displayed, but once I click in the UITextView it automatically scrolls up a bit and the top half of the characters on the first line becomes invisible.
This image is when the UITextView is not active:
UITextView not active http://gerodt.homeip.net/uitextview-notactive.png
And this one is when I clicked in the UITextView to make it active:
UITextView active http://gerodt.homeip.net/uitextview-active.png
I do not the UITextView to scroll up at all, it should simple stay fixed. How can I achieve this? I already tried several settings in Interface Builder, but no luck so far.
Any suggestions are appreciated.
Gero
UITextView is a subclass of UIScrollView, so it has a configurable contentInset property. Unfortunately, if you try to change contentInset on a UITextView instance, the bottom edge inset always gets reset to 32. I've run into this before with short UITextView frames and found this to be an issue. I suspect this is what is causing your problem, but you should check the contentInset of your textview in the debugger to be sure.
The workaround/solution is simple: subclass UITextView and override the contentInset method so that it always returns UIEdgeInsetZero. Try this:
//
// BCTextView
//
// UITextView seems to automatically be resetting the contentInset
// bottom margin to 32.0f, causing strange scroll behavior in our small
// textView. Maybe there is a setting for this, but it seems like odd behavior.
// override contentInset to always be zero.
//
#interface BCZeroEdgeTextView : UITextView
#end
#implementation BCZeroEdgeTextView
- (UIEdgeInsets) contentInset { return UIEdgeInsetsZero; }
#end
This is how UITextView behaves according to Apple's engineer this is intended and UITextView is meant for text that are at least a few lines in height. There is no work around to this, use a UITextField instead or increase your UITextView to at least 3 lines in height.
You can also just do:
textView.contentInset=UIEdgeInsetsZero;
in your delegate file.
UITextView is a subclass of UIScrollView, so the answer involves the contentOffset property, which is what is being changed, not the insets or the content size. If the scroll position is correct when the view first appears, then you can store the content offset for later recall.
YourViewController.h snipped
#interface YourViewController : UIViewController <UITextViewDelegate, UIScrollViewDelegate>
#property(nonatomic, weak) IBOutlet UITextView *textView;
#end
YourViewController.m snippet
#implementation YourViewController {
#private
BOOL _freezeScrolling;
CGFloat _lastContentOffsetY;
}
// UITextViewDelegate
- (void)textViewDidBeginEditing:(UITextView *)textView {
// tell the view to hold the scrolling
_freezeScrolling = YES;
_lastContentOffsetY = self.textView.contentOffset.y;
}
// UITextViewDelegate
- (void)textViewDidEndEditing:(UITextView *)textView {
_freezeScrolling = NO;
}
// UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (_freezeScrolling) {
// prevent the scroll view from actually scrolling when we don't want it to
[self repositionScrollView:scrollView newOffset:CGPointMake(scrollView.contentOffset.x, _lastContentOffsetY)];
}
}
// UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
// scroll prevention should only be a given scroll event and turned back off afterward
_freezeScrolling = NO;
}
// UIScrollViewDelegate
- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView {
// when the layout is redrawn, scrolling animates. this ensures that we are freeing the view to scroll
_freezeScrolling = NO;
}
/**
This method allows for changing of the content offset for a UIScrollView without triggering the scrollViewDidScroll: delegate method.
*/
- (void)repositionScrollView:(UIScrollView *)scrollView newOffset:(CGPoint)offset {
CGRect scrollBounds = scrollView.bounds;
scrollBounds.origin = offset;
scrollView.bounds = scrollBounds;
}
What's also important to note in the code sample above is the last method. Calling any sort of setContentOffset: will actually trigger scrolling, which results in calling scrollViewDidScroll:. So calling setContentOffset: results in an infinite loop. Setting the scroll bounds is the workaround for this.
In a nutshell, we tell the view controller to prevent the UITextView from scrolling when we detect that the user has selected the text for editing. We also store the current content offset (since we know that the position is what we want). If the UITextView tries to scroll, then we hold the content offset in place until the scroll has stopped (which triggers either scrollViewDidEndDecelerating: or scrollViewDidEndScrollingAnimation:). We also unfreeze the scrolling when the user is done editing.
Remember, this is a basic example, so you'll need to tweak the code based on the exact behavior you want.
I was experiencing a similar issue with undesired UITextView scrolling. I finally managed to fix it by resetting the contentSize at the end of my keyboardDidShow:
- (void)keyboardDidShow:(NSNotification *)notification {
textView.contentSize = CGSizeZero;
}
You also will need to register for the keyboard notification, like so:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyboardDidShow:) name:UIKeyboardDidShowNotification object:nil];
In my case I didn't want any scrolling since I was resetting the frame to the height of the textView's contentSize when textViewDidChange (growing textview inside a UIScrollView).
Try putting in Redraw on the textview instead of Scale to Fill. You still might have to capture the delegate and keep the content offset but it should at least prevent the jump to point (0,0). Also Autoresizes subview must be turned off. It was jumping to top of textview every time on me too and this solved that problem.