UITextView content inset - ios

I have encountered something a bit strange with contentInsets
I have a UITextView in my storyboard with a contentInset of 50 left, as I'm trying to add some padding to my uitextview
However, a scrollbar appears on the bottom of the uitextview, as shown below in this test:
I was under the impression that contentInset squashes the uitextview without causing this horizontal scroll bar, so how can I remove the need for the horizontal scrollbar and make everything--the inset AND all the text in the uitextview--visible without the need for this scrollbar.
N.B: I'm not asking about preventing the scrolling horizontally or not displaying the scrollbar(thus cutting of the text)
Thanks a lot!
For atomk(UITextView is called ss)
NSLog(#"Content Size Before %f",self.ss.contentSize.width); Logs: 280
CGSize size=self.ss.contentSize; size.width=size.width-50;
[self.ss setContentSize:size];
NSLog(#"Content Size After %f",self.ss.contentSize.width); Logs: 230
There is no visible difference between the view with the code added than before it was added, so something's going wrong!
(Thanks)

In iOS 7 UITextView is based on TextKit and has a new property textContainerInset. It behaves just as you would expect:
UITextView *textView = ...;
// Left inset of 50 points
textView.textContainerInset = UIEdgeInsetsMake(0.0, 50.0, 0.0, 0.0);
Swift 4.2
textView.textContainerInset = UIEdgeInsets(top: 0, left: 50, bottom: 0, right: 0)

UPDATE: This solution is out of date as of iOS 7.
See this answer below. In iOS 7.0, the textContainerInset property on UITextView was introduced.
Objective-C:
textView.textContainerInset = UIEdgeInsetsMake(0, 50, 0, 0);
Swift:
textView.textContainerInset = UIEdgeInsets(top: 0, left: 50, bottom: 0, right: 0)
Or as Zeev Vax suggested, in Swift 5.0:
textView.textContainerInset.left = 50
Pre-iOS 7 solution:
I was under the impression that contentInset squashes the uitextview without causing this horizontal scroll bar...
I'm afraid this is not how contentInset works with a UITextView. See Apple's documentation for contentInset where it states:
The distance that the content view is inset from the enclosing scroll view... Use this property to add to the scrolling area around the content.
The contentInset is added around the content.
You can change the contentSize in viewDidLayoutSubviews using the code you have included above:
- (void)viewDidLayoutSubviews
{
self.textView.contentInset = UIEdgeInsetsMake(0, 50, 0, 0);
NSLog(#"Content Size Before %f",self.textView.contentSize.width); //Logs: 280
CGSize size=self.textView.contentSize;
size.width=size.width-50;
[self.textView setContentSize:size];
NSLog(#"Content Size After %f",self.textView.contentSize.width); //Logs: 230
}
However, this causes the text to be cut off on the right side:
The best way I have been able to achieve the appearance of horizontal padding in a UITextView is to position it inside a container UIView. In your case, simply create a UIView the same size as your current text view, and add a text view that is 50px narrower inside the container view.
This workaround can cause problems if you have a background for your text view, but from your screenshot above it doesn't look like that's an issue for you.
UITextView (frame in red) inside UIView container:
If your UITextView does have a background, see:
How to set UITextView's content inset like Notes App
Stuff you learn from reverse-engineering Notes.app (see "iPadding" section)

Related

UITableView Cell should not scroll horizontally swift iOS

I have a table view which has card kind of cells as shown in picture.
I have set the content Inset so that I can get the cells with 20px spacing in left,right and top.
tableVw.contentInset = UIEdgeInsets(top: 20, left: 20, bottom: 0, right: 20)
Its displaying as I expected but the problem is, cells can be moved in all direction. But when I move it to right/top, it automatically comes back to original position. But when I move to left it just goes inside and don't scroll back to original position as shown in picture.
I don't want the cells to move at all or I want the cells to come back to centre if its dragged anywhere also. Please help!
Dont provide uiedgeinsets to tableview instead add a view in uitableview cell that cover up the whole cell and add another uiview inside that view and give constraint from top bottom leading trailing equals to 8 or whatever you want then the cell wont move anyways and u tableview cells will look like it has edgeinsets.
you need to set the clipsToBounds property true
tableview.clipsToBounds = true
If you're using AutoLayout, by setting this only should work for you:
In code:
tableView.alwaysBounceVertical = false
or In Interface Builder:
Just find this option and untick "Bounce Vertically" option.
Here's the reference:
If you're not using AutoLayout:
override func viewDidLayoutSubviews() {
// Enable scrolling based on content height
tableView.isScrollEnabled = tableView.contentSize.height > tableView.frame.size.height
}
and also try clipToBounds
tableview.clipsToBounds = true
I achieved what I wanted by below code.
postsTable.contentInset = UIEdgeInsets(top: 20, left: 20, bottom: 0, right: -20)
And in UITableViewCell class :
override var frame: CGRect{
get {
return super.frame
}
set(newFrame){
var frame = newFrame
frame.size.width = kScreenWidth - 40
super.frame = frame
}
}
Now if I drag the cell left or right also its coming back to original position.

iOS: Table view, allow scrolling "further down" than the end

Sorry the question sounds a bit confusing. I have two buttons overlapping my table view at the bottom, so if the table view scrolls "normal" the last row is partially hidden by these buttons. That's why I want to allow scrolling the table like the height of one row further down, so the last row is on top of these two buttons. How can I achieve this?
Adjust the content insets of the table view.
For instance, if your buttons are 50 points in height and your table's frame is the full window, you could set your table to snap to the top of your buttons like this:
tableView.contentInset = UIEdgeInsetsMake(0, 0, 50, 0);
Note: In iOS 7+ view controllers have a property automaticallyAdjustsScrollViewInsets that is set to YES by default. When this property is set to YES, the contentInsets you set manually may be overridden. Assuming you have a nav bar of some kind that you want to scroll under, you can set your top edge inset to the length of the topLayoutGuide.
Your final solution (put this in viewDidLoad):
self.automaticallyAdjustsScrollViewInsets = NO;
tableView.contentInset = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, 50, 0);
As pointed out by others, deprecations have made this solution impossible, and if we use a section footer, this will display at the inset all the time. A much simpler solution would be to add a tableFooterView to allow the bottom cells to scroll past the buttons. Like so:
let bottomView = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 83))
bottomView.backgroundColor = .clear
tableView.tableFooterView = bottomView
This will make it so the contents shift up.
If you want them to shift down you can change the insets accordingly.
func shiftScrollingUp() {
yourScrollView.contentInsetAdjustmentBehavior = .never
yourScrollView.contentInset = UIEdgeInsetsMake(0, 0, 150, 0)
}
AutomaticallyAdjustsScrollViewInsets is deprecated in ios 11.

UITextView's Text Overlaps When Scrolled

I have a UILabel and UITextView added into a UIViewController's view like in the screenshot below.
When I typed some words and uitextview starts scrolling, it overlaps the title of the label like below.
I tried to set textView's textContainerInset but it didn't work as I expected. It started the text 10pt below the normal position but still overlapped when it was scrolled.
textView.textContainerInset = UIEdgeInsetsMake(10, 0, 0, 0);
It seems that you have place TextView upon UILabel. Move the UILabel up or TextView down, hope that will solve the problem.
CGRect frame = theLabel.frame;
frame.origin.y = frame.origin.y - 20;
theLabel.frame= frame;
This will move the label 20px up.
Hope this helps... :)

UICollectionView header width

In my UICollectionViewFlowLayout subclass I have this:
self.headerReferenceSize = CGSizeMake(280, 44);
However the header is displayed with a width of 320, which is the width of the collection view. This is correct according to the docs:
During layout, only the size that corresponds to the appropriate
scrolling direction is used. For example, for the vertical scrolling
direction, the layout object uses the height value returned by your
method. (In that instance, the width of the header would be set to the
width of the collection view.)
However, I need the width to be 280, and not stretch to the entire width of the collection view. How can I override this?
You can add another custom view over header and resize it as you wish. Ofcourse all you're components that were on header will go over custom view you created...
You don't need to add a subview to the header.
You want a header width of 280 in a Collection View of 320, so apply a Content Inset of 20 on the left and right, like so:
Swift
YourCollectionView.ContentInset = UIEdgeInsets(top: 0, left: 20, bottom: 20, right: 0)
Xamarin
YourCollectionView.ContentInset = new UIEdgeInsets(0, 20, 0, 20);

Autolayout - intrinsic size of UIButton does not include title insets

If I have a UIButton arranged using autolayout, its size adjusts nicely to fit its content.
If I set an image as button.image, the instrinsic size again seems to account for this.
However, if I tweak the titleEdgeInsets of the button, the layout does not account for this and instead truncates the button title.
How can I ensure that the intrinsic width of the button accounts for the inset?
Edit:
I am using the following:
[self.backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)];
The goal is to add some separation between the image and the text.
You can get this to work in Interface Builder (without writing any code), by using a combination of negative and positive Title and Content Insets.
Update: Xcode 7 has a bug where you cannot enter negative values in the Right Inset field, but you can use the stepper control next to it to decrease the value. (Thanks Stuart)
Doing this will add 8pt of spacing between the image and the title and will increase the intrinsic width of the button by the same amount. Like this:
You can solve this without having to override any methods or set an arbitrary width constraint. You can do it all in Interface Builder as follows.
Intrinsic button width is derived from the title width plus the icon width plus the left and right content edge insets.
If a button has both an image and text, they’re centered as a group, with no padding between.
If you add a left content inset, it’s calculated relative to the text, not the text + icon.
If you set a negative left image inset, the image is pulled out to the left but the overall button width is unaffected.
If you set a negative left image inset, the actual layout uses half that value. So to get a -20 point left inset, you must use a -40 point left inset value in Interface Builder.
So you provide a big enough left content inset to create space for both the desired left inset and the inner padding between the icon and the text, and then shift the icon left by doubling the amount of padding you want between the icon and the text. The result is a button with equal left and right content insets, and a text and icon pair that are centered as a group, with a specific amount of padding between them.
Some example values:
// Produces a button with the layout:
// |-20-icon-10-text-20-|
// AutoLayout intrinsic width works as you'd desire.
button.contentEdgeInsets = UIEdgeInsetsMake(10, 30, 10, 20)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)
Why not override the intrinsicContentSize method on UIView? For example:
- (CGSize) intrinsicContentSize
{
CGSize s = [super intrinsicContentSize];
return CGSizeMake(s.width + self.titleEdgeInsets.left + self.titleEdgeInsets.right,
s.height + self.titleEdgeInsets.top + self.titleEdgeInsets.bottom);
}
This should tell the autolayout system that it should increase the size of the button to allow for the insets and show the full text. I'm not at my own computer, so I haven't tested this.
You haven't specified how you're setting the insets, so I'm guessing that you're using titleEdgeInsets because I see the same effect you're getting. If I use contentEdgeInsets instead it works properly.
- (IBAction)ChangeTitle:(UIButton *)sender {
self.button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
[self.button setTitle:#"Long Long Title" forState:UIControlStateNormal];
}
And for Swift worked this:
extension UIButton {
override open var intrinsicContentSize: CGSize {
let intrinsicContentSize = super.intrinsicContentSize
let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom
return CGSize(width: adjustedWidth, height: adjustedHeight)
}
}
Love U Swift
This thread is a bit old, but I just ran into this myself and was able to solve it by using a negative inset. For example, substitute your desired padding values here:
UIButton* myButton = [[UIButton alloc] init];
// setup some autolayout constraints here
myButton.titleEdgeInsets = UIEdgeInsetsMake(-desiredBottomPadding,
-desiredRightPadding,
-desiredTopPadding,
-desiredLeftPadding);
Combined with the right autolayout constraints, you end up with an auto-resizing button which contains an image and text! Seen below with desiredLeftPadding set to 10.
You can see that the actual frame of the button doesn't encompass the label (since the label is shifted 10 points to the right, outside the bounds), but we've achieved 10 points of padding between the text and the picture.
I wanted to add a 5pt space between my UIButton icon and the label. This is how I achieved it:
UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
// more button config etc
infoButton.contentEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 5);
infoButton.titleEdgeInsets = UIEdgeInsetsMake(0, 5, 0, -5);
The way contentEdgeInsets, titleEdgeInsets and imageEdgeInsets relate to each other requires a little give and take from each inset. So if you add some insets to the title's left you have to add negative inset on the right and provide some more space (via a positive inset) on the content right.
By adding a right content inset to match the shift of the title insets my text doesn't go outside the bounds of the button.
For Swift 3 based on pegpeg's answer:
extension UIButton {
override open var intrinsicContentSize: CGSize {
let intrinsicContentSize = super.intrinsicContentSize
let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom
return CGSize(width: adjustedWidth, height: adjustedHeight)
}
}
All above did not work for iOS 9+, what i did is:
Add a width constraint (for a minimum width when the button doesn't have any text. The button will auto scale if text is provided)
set the relation to Greater Than or Equal
Now to add a border around the button just use the method:
button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
The option is also available in interface builder. See the Inset. I set left and right to 3. Works like a charm.
The solution I use is to add a width constraint on the button. Then somewhere in initialization, after your text is set, update the width constraint like so:
self.buttonWidthConstraint.constant = self.shareButton.intrinsicContentSize.width + 8;
Where 8 is whatever your inset is.

Resources