UITextView - how to disable highlighting? - objective-c

I disabled editing, but how do I disable the selection interface from popping up? I don't want the user to be able to highlight text, or copy it etc.
It is important to preserve vertical scrolling.

textView.userInteractionEnabled = NO;
Ok then if you need to preserve scrolling, then create a scrollView, set it as an outlet and add the textview on it.
Then in viewWillAppear set the textview's height:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.descriptionTextView.text = #""; // set some text to determine the total height
self.descriptionTextView.frame = CGRectMake(self.descriptionTextView.frame.origin.x,
self.descriptionTextView.frame.origin.y,
self.descriptionTextView.frame.size.width,
self.descriptionTextView.contentSize.height);
self.descriptionTextView.userInteractionEnabled = NO;
//next set new content size for scroll view
self.descriptionScrollView.contentSize = CGSizeMake(self.descriptionScrollView.frame.size.width,
self.descriptionTextView.frame.origin.y + self.descriptionTextView.frame.size.height);
}

Swift 4, Xcode 10
Make sure you set the delegate to YourViewController
yourTextView.delegate = yourViewControllerInstance
Then this will disable highlighting, enable tapping links and allow scrolling
extension YourViewController: UITextViewDelegate {
func textViewDidChangeSelection(_ textView: UITextView) {
view.endEditing(true)
}
}

Related

Not editable NSTextfiled, to make it auto resize when string is too long, like english word to French

As my question being posted, in my textfield, when I open my app in English, it works well, but when switching to French, we know French word is quite long sometimes and in that case, words showing in the textfield will be cut off.
I tried some customised way for textfield on stack overflow, but it works well for editable textfield, when I made it none-editable, the behaviour will be wired.
It's like when the word is too long, it just make textfield longer, meaning just increase width, not height. What I'm expecting is keeping width fix while changing height when word is too long.
intrinsicSize = [super intrinsicContentSize];
NSTextView *textView = (NSTextView *)fieldEditor;
NSRect usedRect = [textView.textContainer.layoutManager usedRectForTextContainer:textView.textContainer];
kind of two main func I used, when it's editable, everything went well and height changes while width fix, but when it's none editable, only width changes.
Any advice?
In your NSTextField subclass override following methods. Also make sure that NSTextField is set to wrap in IB. Add constraints to it.
-(NSSize)intrinsicContentSize
{
// If wrap is not enabled return the original size
if ( ![self.cell wraps] ) {
return [super intrinsicContentSize];
NSRect frame = [self frame];
CGFloat width = frame.size.width;
// This will allow to grow it in height and not width
frame.size.height = CGFLOAT_MAX;
CGFloat height = [self.cell cellSizeForBounds: frame].height;
// return the calculated height
return NSMakeSize(width, height);
}
// Listen to text change notification and invalidate the default size
- (void)textDidChange:(NSNotification *)notification
{
[super textDidChange:notification];
[self invalidateIntrinsicContentSize];
}

Editable transparent NSTextField text appears with white highlight

I am trying to create editable transparent NSTextField in a semi transparent window:
What I have noticed is that whenever the field is editable there is a white "selection like" background drawn even though the element is not actually selected.
Additional observable symptoms:
This highlight is not present when the field is set as non-editable.
If there are multiple fields only the first one has the highlight.
The highlight is not present if the text is not set programmatically
Following code was used to generate the field:
f = [[NSTextField alloc] initWithFrame:b2];
f.backgroundColor = [NSColor clearColor];
f.drawsBackground = YES;
f.bordered = NO;
f.bezeled = NO;
f.focusRingType = NSFocusRingTypeNone;
f.textColor = [NSColor whiteColor];
f.editable = YES;
f.selectable = YES;
f.backgroundColor = [NSColor clearColor];
f.allowsEditingTextAttributes = YES;
f.stringValue = #"Foo";
[self.contentView addSubview:f];
Additional observations (potentially a separate problem):
When field is not the first field on the screen and the initial text is set programmatically and removed by editing the field there is a shadow of the text:
I can't seem to find any documentation on this I wonder if any of you have had this happen and potentially have a solution or a pointer to docs I might have not stumbled upon.
part 1: removing highlight
there are two options here depending on the behavior you are looking for
option 1 - nil first responder
TextField is not first responder
No highlighted text
No Cursor at the end of text
Assuming you are using an NSWindow, set the first responder to nil after calling makeKeyAndOrderFront
[self.window makeKeyAndOrderFront:self];
[self.window makeFirstResponder:nil];
It appears as though makeKeyAndOrderToFront: looks for the first NSResponder in the window willing to accept first responder. Then becomeFirstResponder is called on that responder; leading to option 2
option 2 - override becomeFirstResponder
TextField is first responder
No highlighted text
Cursor appears at the trailing edge of text
Subclass NSTextfield and override it's becomeFirstResponder method
#implementation BPTextField
- (BOOL)becomeFirstResponder {
BOOL isResponder = [super becomeFirstResponder];
//Get Field editor, set selected range
NSText* fieldEditor = [[self window] fieldEditor:YES forObject:self];
[fieldEditor setSelectedRange:NSMakeRange(fieldEditor.string.length ,0)];
return isResponder;
}
#end
I prefer this option from a usability perspective
part 2: removing shadow
option 1 - add a solid background color
I'm not clear ; ) on why this is the case, but if you add a solid background color, the text will update.
option 2 - override textDidChange
override textDidChange:notification in your textfield
#implementation BPTextField
- (void)textDidChange:(NSNotification *)notification {
[super textDidChange:notification];
[self setNeedsDisplay:YES];
}
#end
Final notes
You'll notice that the text looks bad, or rigid. Adding a background color to the textfield, or to the superview's layer will fix this.
This is an answer to part 2 of the question.
The shadow artifact is from rendering window's shadow which is not updated when the text in the NSTextField changes.
If the window's hasShadow method returns "NO" the text's shadow will not create shadow for the text either.

Added whitespace in UIWebview - removing UIWebView whitespace in iOS7 & iOS8

Im loading local html files, since iOS7 there is added white space on top in the UIWebView.(I cant post an image as i do not have enough points.)
image can be seen here- snap shot from iPhone simulator, uiwebview surrounded by black frame, the html content is grey, but there is white added above it
I have tried to adjust the zoom using
[webView stringByEvaluatingJavaScriptFromString:#"document. body.style.zoom = 5.0;"];
webView.scalesPageToFit = NO;
credit to: Srikar Appal
I also set tried to remove white spacing:
NSString *padding = #"document.body.style.margin='0';document.body.style.padding = '0'";
[webView stringByEvaluatingJavaScriptFromString:padding];
credit to: thenextmillionaire
still no luck. In the desktop chrome browser there is no whitespace. The html files are Google Swiffy files - containing html and JSON.
edit: updated Image
Try self.automaticallyAdjustsScrollViewInsets = NO; in ViewDidLoad.
ios 7 add 64px automatically for scroll view. (status bar and nav bar)
This problem only affects the UIWebView if it is the first subview of the parent view. One alternative way to work around this problem is to add another non-visible empty view to the parent view as the first view. In Interface Builder add a zero size subview and use the Editor->Arrange->Send to Back menu command.
If you're not using Interface Builder, but instead are subclassing the UIWebView, then it can be done by creating a UIView instance variable called scrollFixView and overriding the following methods:
- (void)didMoveToSuperview
{
[super didMoveToSuperview];
if ([self superview].subviews.firstObject == self) {
_scrollFixView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
_scrollFixView.hidden = YES;
[[self superview] insertSubview:_scrollFixView belowSubview:self];
}
}
- (void)removeFromSuperview
{
if (_scrollFixView) {
[_scrollFixView removeFromSuperview];
_scrollFixView = nil;
}
[super removeFromSuperview];
}
I had the same problem so I tried a few things:-)
This worked for me, but correct me please if there is a better way.
-(void)webViewDidFinishLoad:(UIWebView *)webView
{
if(self.navigationController.navigationBar.translucent == YES)
{
_webView.scrollView.contentOffset = CGPointMake(_webView.frame.origin.x, _webView.frame.origin.y - 54);
}
}
So basically you need to :
1) Add the UIWebView delegate method - webViewDidFinishLoad:
2) Then I setup an if statement to check if the translucent option is active.
The last one you only need to do of course if you give the user the option within your app.
The number after the _webView.frame.origin.y is just for my app. It may differ for you.
I solved this problem by simply setting a constraint on the WebView, setting the top space between it and the View top to 0, causing the NavBar to overlap the whitespace.
One alternative to Jeff Kranenburg's method is to subclass and override the UIWebView subclasses' UIScrollViewDelegate method scrollViewDidScroll:. This is only appropriate if scrolling is turned off for your subclass.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if ([[self superclass] instancesRespondToSelector:_cmd]) {
[super scrollViewDidScroll:scrollView];
}
[self fixUpScrollViewContentOffset];
}
- (void)fixUpScrollViewContentOffset
{
if (!CGPointEqualToPoint(self.scrollView.contentOffset, CGPointZero)) {
self.scrollView.contentOffset = CGPointZero;
}
}
I already got it .
here my logic code, When the application open the website you must get the size of your webview then set it on height
here my code
ViewGroup.MarginLayoutParams p = (ViewGroup.MarginLayoutParams) webpage.getLayoutParams();
p.height = webpage.getHeight();
// check if how long you need to set your height for webpage then set it :)
Log.e(" webpage.getHeight()", String.valueOf(webpage.getHeight()));
webpage.setLayoutParams(p);
Hope you will take my code and my answer to :) works on any devices even tabs too :)

Intercepting UIScrollView Scrolls

I have a UIScrollView and a UITableView inside the UIScrollView.
I would like to intercept scrolling of the UITableView and only allow scrolling if the super view (UIScrollView) have reached a specific contentOffset.
I have created subclass of both UIScrollView and UITableView, how do i catch scrolling event and intercepting the scrolling while the user is still scrolling?
Example of what i'm trying to accomplish:
The UITableView is going to have a header, if i scroll down the header will collapse to 30% of original size and and stay visible at the top. After i have scrolled back to the top of the UITableView i want the header to expand. In other word i want to extend the scrolling of a UITableView with a header that can collapse/expand.
There might be better way to accomplish this, i'm open for suggestions.
There may be a better way, but this solution is working for me.
UITableView (let's call it innerScrollView) inside of UIScrollView (let's call it scrollView) inside whatever the main view is (let's call it view).
Set an outlet in your view controller for the innerScrollView (self.innerScrollView).
Set bounce in XIB or code to: scrollView.bounces = YES and innerScrollView.bounces = NO.
Set scrolling to disabled initially for the innerScrollView.
In code (viewDidLoad:) set scrollView.contentSize.height to view.frame.size.height + the 70% of the header you want to disappear. Resize innerScrollView to fill the resized contentView to the bottom.
Set your view controller as the delegate for scrollView and implement scrollViewDidScroll: as follows:
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat maxYOffset = self.scrollView.contentSize.height - self.scrollView.frame.size.height;
CGFloat minInnerYOffset = self.innerScrollView.contentInset.top;
CGFloat currentYOffset = scrollView.contentOffset.y;
CGFloat newYOffsetDelta = currentYOffset - maxYOffset;
CGFloat currentInnerYOffset = self.innerScrollView.contentOffset.y;
if (scrollView.contentOffset.y >= maxYOffset) {
self.innerScrollView.scrollEnabled = YES;
self.scrollView.contentOffset = CGPointMake(0, maxYOffset);
if (currentYOffset != maxYOffset) {
self.innerScrollView.contentOffset = CGPointMake(0, currentInnerYOffset + newYOffsetDelta);
}
}
else if (currentInnerYOffset > minInnerYOffset) {
self.scrollView.contentOffset = CGPointMake(0, maxYOffset);
self.innerScrollView.contentOffset = CGPointMake(0, currentInnerYOffset + newYOffsetDelta);
}
else if (currentYOffset < scrollView.contentInset.top) {
scrollView.contentOffset = CGPointMake(0, scrollView.contentInset.top);
}
else {
self.lowerScrollView.scrollEnabled = NO;
}
}
I just whipped this up and quickly tested it. It all seems to work, but there may be some improvements and tweaks needed. Regardless, this should get you started at least.
Well, you should use the following UIScrollViewDelegate method
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
// here perform the action you need
}

UIScrollView contentSize not working

I put a UIScrollView in my nib's view, and linked it to a an IBOutlet property.
Now, when I do this in my viewDidLoad method, it seems to have no effect on the contentSize:
self.sv.backgroundColor = [UIColor yellowColor]; // this works
CGSize size = CGSizeMake(1000.0, 1000.0);
[self.sv setContentSize:size]; // this does not
It behaves as if the contentSize was the same as the frame. What's going on?
This started working when I turned off AutoLayout. Why?
I had the same problem. Auto Layout for UIScrollView is messed up.
Work around: Put everything in the UIScrollView into another UIView, and put that UIView as the only child of the UIScrollView. Then you can use Auto Layout.
If things near the end is messed up (the end of whichever direction your UIScrollView scrolls), change the constraint at the end to have the lowest possible priority.
I tried viewWillLayoutSubviews to update scrollView's contentSize, it worked for me.
- (void)viewDidLayoutSubviews
{
[self.bgScrollView setContentSize:CGSizeMake(320, self.view.frame.size.height* 1.5)];
}
Apple Doc
-(void)viewDidLayoutSubviews
Called to notify the view controller that its view has just laid out its subviews.
Discussion
When the bounds change for a view controller’s view, the view adjusts the positions of its subviews and then the system calls this method. However, this method being called does not indicate that the individual layouts of the view’s subviews have been adjusted. Each subview is responsible for adjusting its own layout.
Your view controller can override this method to make changes after the view lays out its subviews. The default implementation of this method does nothing.
The easiest/cleanest way is to set contentSize at viewDidAppear so you negate the effects of autolayout. This doesn't involve adding random views. However relying on load order for an implementation to work may not be the best idea.
Use this code. ScrollView setContentSize should be called async in main thread.
Swift:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
DispatchQueue.main.async {
var contentRect = CGRect.zero
for view in self.scrollView.subviews {
contentRect = contentRect.union(view.frame)
}
self.scrollView.contentSize = contentRect.size
}
}
Objective C:
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
dispatch_async(dispatch_get_main_queue(), ^ {
CGRect contentRect = CGRectZero;
for(UIView *view in scrollView.subviews)
contentRect = CGRectUnion(contentRect,view.frame);
scrollView.contentSize = contentRect.size;
});
}
There are two problems here. (1) viewDidLoad is too soon; you have to wait until after layout has taken place. (2) If you want to use autolayout with a scrollview that comes from a nib, then either you must use constraints to completely describe the size of the contentSize (and then you don't set the contentSize in code at all), or, if you want to set it in code, you must prevent the constraints on the scrollview's subviews from dictating the contentSize. It sounds like you would like to do the latter. To do so, you need a UIView that acts as the sole top-level subview of the scrollview, and in code you must set it to not use autolayout, enabling its autoresizingMask and removing its other external constraints. I show an example of how to do that, here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/ch20p573scrollViewAutoLayout/ch20p573scrollViewAutoLayout/ViewController.m
But notice also the next example, which shows how to use constraints completely, instead of contentSize.
A SUPER easy way to use AutoLayout with UIScrollViews inside Interface Builder:
Step 1: Create a UIScrollView
Step 2: Create a UIView that is a child of your scroll view like so:
-UIScrollView
---UIView
-----Your other content
(We'll call this one contentView).
Step 3: In the size inspector, give this view a height and width (say, 320x700).
Step 4 (using AutoLayout): Create unambiguous constraints from your contentView to its superview (the UIScrollView): connect the 4 edges (top, leading, trailing, bottom), then give it a defined width and height that you want it to scroll too.
For example: If your scroll view spans the entire screen, you could give your content view a width of [device width] and a height of 600; it will then set the content size of the UIScrollView to match.
OR:
Step 4 (not using AutoLayout): Connect both of these new controls to your view controller using IB (ctrl+drag from each control to your view controller's .h #implementation). Let's assume each is called scrollView and contentView, respectively. It should look like this:
#interface YourViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
#property (strong, nonatomic) IBOutlet UIView *contentView;
#end
Step 5 (not using AutoLayout): In the view controller's .h file add (actually, override) the following method:
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
self.scrollView.contentSize = self.contentView.frame.size;
}
You can use this lines of code into your *.m file's
- (void)viewDidLoad{
[scroll setContentSize:CGSizeMake(320, 800)] ;
[scroll setScrollEnabled:TRUE];
[scroll setShowsVerticalScrollIndicator:NO];
[scroll setShowsHorizontalScrollIndicator:YES];
}
for this you need to take an IBOutlet property of UIScrollView into your *.h file this way:
IBOutlet UIScrollView *scroll;
And connect this from Storyboard.
Or,
You can use this method into your *.m file:
-(void)viewDidLayoutSubviews
{
[scroll setContentSize:CGSizeMake(320, self.view.frame.size.height* 1.5)];
// this will pick height automatically from device's height and multiply it with 1.5
}
This both solution works for me in xcode-5, xcode-6, xcode-6.1, xcode-6.2
Setting the contentSize in viewDidAppear is critical.
But I also had a variation of what worked in the 3.5 inch screen, and the 4 inch screen. The 4 inch screen worked, the older one does not. Both iOS 7. Bizarre is an understatement!
I could never get auto layout based on constraints to work. Since my view was already a subclass UIScrollView I solved it by overriding setContentView: and ignoring auto layouts zero height setContentSize: message.
#interface MyView : UIScrollView {}
#end
#implementation MyView
- (void)setContentSize:(CGSize)aSize {
if (aSize.height > 0)
[super setContentSize:aSize];
}
#end
I used to do set up the uiscrollview programmatically UNTIL I watched the following wonderful tutorial, step by step how to get uiscrollview and uiview to work: https://www.youtube.com/watch?v=PgeNPRBrB18
After watching the video you will start liking Interface Builder I am sure.
Vote up
Still not scrolling when dynamic height of labels exceeds view height.
I did what yuf's answer marked as correct above said to do (I added a content view to my scrollview and set the constraints leading, trailing, top bottom, and equal widths from the content view to the scroll view.) but still my view was not scrolling when the internal controls height exceeded the height of the scrollview.
Inside my content view I have an image and 3 labels below it. Each label adjusts their own height dependant on how much text is in them (they are set to word-wrap and numberoflines = 0 to achieve this).
The problem I had was my content view's height was not adjusting with the dynamic height of the labels when they exceeded the height of the scroll view/main view.
To fix this I worded out I needed to set the Bottom Space to Container constraint between my bottom label and the contentview and gave it a value of 40 (chosen arbitrarily to give it a nice margin at the bottom). This now means that my contentview adjusts its height so that there is a space between the bottom of the last label and itself and it scrolls perfectly!
Yay!
Try this out...
add all constraints like you do for UIView (See screenShot of my ViewControler in Storyboard)
Now trick begins. select your last object and select its bottom constraint. (See above screenShot, Instagram button's Bottom Constraint(Yellow line)) and Change the Constant in Size Inspector like in bellow screenshot.
i require Constant=8 but you can change as per your requirements.
this Constant is the Space between That Orange Button's Bottom and the scrollView.
EDIT
Make Sure about your view's hierarchy .
0) ViewController.view (optional)
1) UIScrollView
2) UIView (Rename as "contentView")
3) UIView (this view is your content that will make scrollView scroll)
I finally worked out my own solution to this problem because in my case I couldn't use the view controller's life cycle. Create your own scroll view subclass and use it instead of UIScrollView. This even worked for a scroll view inside a collection view cell.
class MyScrollView:UIScrollView {
var myContentSize:CGSize = CGSize.zero // you must set this yourself
override func layoutSubviews() {
super.layoutSubviews()
contentSize = myContentSize
}
}
My MyScrollView was defined in the nib with a tag of 90. If so this is a good way to set content size in the code in the parent view.
let scrollView = viewWithTag(90) as! MyScrollView
scrollView.myContentSize = ...
If you are using AutoLayout a really easy way to set the contentSize of a UIScrollView is just to add something like this:
CGFloat contentWidth = YOUR_CONTENT_WIDTH;
NSLayoutConstraint *constraintWidth =
[NSLayoutConstraint constraintWithItem:self.scrollView
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeLeading
multiplier:1
constant:contentWidth];
[self.scrollView addConstraint:constraintWidth];
I got Autolayout to work for paginated scroll views whose pages occupy the full-width of the screen. The pages automatically resize according to the scroll view's size. I haven't tested this for lesser-width scroll views but do comment away if it works--I beleieve it should. Targeted for iOS 9, wrote code in Swift 2, used a mix of IB's and custom code in awakeFromNib.
Steps:
Define a full-screen scroll view.
Inside the scroll view, add a UIView (I called mine contentView) whose top, trailing, bottom, and leading edges to the scroll view are all zero; the height is equal to the scroll view's; but the width is the scroll view's width times the number of pages. If you're doing this visually, you will see your content view extend beyond your scroll view in Inteface Builder.
For every "page" inside the contentView, add Autolayout rules to put them side-by-side each other, but most importantly, give them each a constraint so that their widths are equal to the scroll view's, not the content view's.
Sample code below. embedChildViewController is just my convenience method for adding child VCs--do look at setupLayoutRulesForPages. I have exactly two pages so the function is too simple, but you can expand it to your needs.
In my view controller:
override func loadView() {
self.view = self.customView
}
override func viewDidLoad() {
super.viewDidLoad()
self.embedChildViewController(self.addExpenseVC, toView: self.customView.contentView, fillSuperview: false)
self.embedChildViewController(self.addCategoryVC, toView: self.customView.contentView, fillSuperview: false)
self.customView.setupLayoutRulesForPages(self.addExpenseVC.view, secondPage: self.addCategoryVC.view)
}
My custom view:
class __AMVCView: UIView {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var contentView: UIView!
#IBOutlet weak var pageControl: UIPageControl!
override func awakeFromNib() {
super.awakeFromNib()
self.scrollView.pagingEnabled = true
self.scrollView.bounces = true
self.scrollView.showsHorizontalScrollIndicator = false
self.scrollView.showsVerticalScrollIndicator = false
self.pageControl.numberOfPages = 2
self.contentView.backgroundColor = UIColor.blueColor()
self.scrollView.backgroundColor = UIColor.clearColor()
self.backgroundColor = UIColor.blackColor()
}
func setupLayoutRulesForPages(firstPage: UIView, secondPage: UIView) {
guard self.contentView.subviews.contains(firstPage) && self.contentView.subviews.contains(secondPage)
else {
return
}
let rules = [
"H:|-0-[firstPage]-0-[secondPage]-0-|",
"V:|-0-[firstPage]-0-|",
"V:|-0-[secondPage]-0-|"
]
let views = [
"firstPage" : firstPage,
"secondPage" : secondPage
]
let constraints = NSLayoutConstraint.constraintsWithVisualFormatArray(rules, metrics: nil, views: views)
UIView.disableAutoresizingMasksInViews(firstPage, secondPage)
self.addConstraints(constraints)
// Add the width Autolayout rules to the pages.
let widthConstraint = NSLayoutConstraint(item: firstPage, attribute: .Width, relatedBy: .Equal, toItem: self.scrollView, attribute: .Width, multiplier: 1, constant: 0)
self.addConstraint(widthConstraint)
}
}