Giving an NSTextView some padding/a margin - objective-c

How would I give a NSTextView some padding/a margin to the left?
I know how you do it in a NSTextField (by subclassing NSTextFieldCell) but how do you do it in a NSTextView?
EDIT: A bit more info:
1. The Text View just has plain text no rich text and no other fancy stuff like a proper text editor (e.g Paragraph insets).
2. Is it possible to use setTextContainerInset: for this?

You could try subclassing NSTextView and override the textContainerOrigin.
Details here.
For example this subclass will give a top and bottom margin of 5 left of 20 and right of 10.
#implementation MyTextView
- (void)awakeFromNib {
[super setTextContainerInset:NSMakeSize(15.0f, 5.0f)];
}
- (NSPoint)textContainerOrigin {
NSPoint origin = [super textContainerOrigin];
NSPoint newOrigin = NSMakePoint(origin.x + 5.0f, origin.y);
return newOrigin;
}
#end

Just to add an update to this. iOS7 adds a property to UITextView called textContainerInset. Calling setTextContainerInset will create margins inside the TextView for the content.

The way TextEdit does it (when in Wrap to Page mode) is to put the text view inside of a larger view, and set that larger view as the document view of the scroll view. That's more work to set up, but won't leak presentation information (in the form of a specially-customized paragraph style) into the model (the text).

Create a mutable paragraph style (most probably by making a mutable copy of the default paragraph style, then set its head indent and first-line head indent to the left margin you want. Then, set this paragraph style as the value of the NSParagraphStyleAttributeName attribute for the entire contents of the view's text storage.
Note that this will show up in RTF and possibly HTML data obtained from/given to you by the view. If the view is not read-only (i.e., the user can edit the text and you will retrieve or receive that text from the view), then you should probably avoid this solution. If the user can show the ruler and edit the paragraph style themselves, then you should definitely avoid this solution.

This is all it takes when using a NSTextView with plain text style :
textView.textContainerInset = NSSize(width: 15, height: 5)

Related

Interface Builder Autolayout and Resizing

So I have two questions about Interface Builder for Xcode:
I'm trying to build an interface with a label on the left and a text field on the right, which have constant spacing between them, but I want the text field to expand horizontally when I resize the window horizontally to keep the spacing. I've added a restraint that keeps the spacing between them equal, but when I move the window, it resizes the label box rather than the text field. I tried pinning the width of the label, but then it stops me from resizing the window.
Is there any way to resize multiple items at the same time? Like if I have 8 labels vertically and I want to size them all to each be an 8th of the window space, how can I do that without just eyeballing it? It would be easy if you could highlight all of them and drag one corner to resize them all, but it wont let me do that.
For your first problem you should uncheck AutoLayout from Utilities Panel -> File Inspector and see what happens. Concerning the second you can create how many labels you want with your desired size directly from code as follows :
Example for 10 labels:
for (int i=0; i<30; i++)
{
UILabel *label = [[UILabel alloc] initWithFrame:anyFrame];
[yourView addSubView:label];
[label release]; //if not using ARC
}

iCarousel and UITextView -- blank text view

I'm using iCarousel to display editable question cards. The cards contain a UITextView for entering the question (or already contain text as you swipe through filled cards). However, when the carousel is presented and scrolled, sometimes text views appear empty.
This is due to a UITextView optimization of not drawing text offscreen. But text views in a UITableView will not suffer from this.
As many know, using setNeedsDisplay will NOT work due to the optimization, so it doesn't redraw the text.
I currently change the text view's frame by adding and then removing 1px. This forces a redraw. However, I can only do this when the item changes. iCarousel does not have a willDisplayCell delegate method. (Nick, can you add one easily? The code baffles me)
Because iCarousel is preloading many views for smoothness (which is necessary, setting iCarouselOptionVisibleItems doesn't fix anything) there doesn't seem to be anything else I can do but know when the view is about to come on screen. Suggestions?
- (UIView *)carousel:(iCarousel *)carousel viewForItemAtIndex:(NSUInteger)index reusingView:(UIView *)view
{
MIQuestionView *questionView = (MIQuestionView *)view;
if (questionView == nil)
{
MIQuestionType type = [self.testSection.questionType integerValue];
questionView = [[MIQuestionView alloc] initWithFrame:CGRectNull questionType:type];
questionView.delegate = self;
}
questionView.question = [self.testSection.questions objectAtIndex:index];
return questionView;
}
The text view is embedded in the MIQuestionView. The text is set in the question setter. There's no way for me to know when it's coming onto the screen. To be clear, I don't want to resize. The text is not drawn offscreen and appear blank when coming on-screen.
Sorry, I didn't look for ambiguous code above:
- (id)initWithFrame:(CGRect)frame questionType:(MIQuestionType)type
{
if (CGRectEqualToRect(frame, CGRectNull))
frame = CGRectMake(0, 0, 650, 244);
self = [super initWithFrame:frame];
...
It's an odd bug. I'm not sure if it's an issue with iCarousel or just a quirk of iOS that you need to deal with when dynamically adding and removing UITextViews from the view hierarchy.
I have a solution that's maybe a bit cleaner than the ones you've found though; Just add this to your MICardView:
- (void)didMoveToSuperview
{
if (self.superview)
{
_textView.frame = self.bounds;
}
}
This basically forces the textView to re-layout every time the cardView is recycled, and it avoids you having to do anything special in your view controller to work around the issue.

I want to set text of CATextLayer on click of button. how can it be achieved?

I made a CALayer *sublayer over the main view, then I added CATextLayer over sublayer.
Now I want to show text on the CATextLayer by clicking a button on my main view.
How can I do this?
Set the buttons action using -[UIButton addTarget:selector:forControlEvents:]:, then in that function set the CATextLayers string property to either an NSString or an NSAttributedString.
I remind you that CATextLayer doesn't respect paragraph settings on attributed strings, so line height is kind of off and does not look so great.

find the location {x,y} of text in uilabel

I have a string coming from server which I am displaying on UILabel multiligne. It is within that string, I am identifying some particular substring. I want to place a button on that substring(button will be a subview of UILabel). For this I require substring coordinates. I went through this but I am not able to understand it. Suppose my complete string is abc, 567-324-6554, New York. I want 567-324-6554 to be displayed on button for which I need its coordinates.
How can I use above link to find coordinates of substring?
Thanks,
UILabel doesn't have any methods for doing this. You can do it with UITextView, because it implements the UITextInput protocol. You will want to set the text view's editable property to NO.
Something like this untested code should work:
- (CGRect)rectInTextView:(UITextView *)textView stringRange:(CFRange)stringRange {
UITextPosition *begin = [textView positionFromPosition:textView.beginningOfDocument offset:stringRange.location];
UITextPosition *end = [textView positionFromPosition:begin offset:stringRange.length];
UITextRange *textRange = [textView textRangeFromPosition:begin toPosition:end];
return [textView firstRectForRange:textRange];
}
That should return a CGRect (in the text view's coordinate system) that covers the substring specified by stringRange. You can set the button's frame to this rectangle, if you make the button a subview of the text view.
If the substring spans multiple lines, the rectangle will only cover the first line.

Getting NSTextView to perfectly fit its contents

I have a view that contains a button and a textview. When the button is clicked, the textview's hidden status will change and be shown on the view. Springs and struts have been configured so the textview expands vertically with the view. All this is done in IB
I then insert text into the textview programmatically, but I need the textview to show all its contents without the user needing to scroll.
This is the code I use to calculate the height of the text in the textview:
- (float) getTextViewHeight {
//based on http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/TextLayout/Tasks/StringHeight.html%23//apple_ref/doc/uid/20001809-CJBGBIBB
[textview.textContainer setLineFragmentPadding:0.0];
[textview.layoutManager glyphRangeForTextContainer:textview.textContainer];
return [textview.layoutManager usedRectForTextContainer:self.interactionData.textContainer].size.height;
}
With or without that call to -sizeToFit on the textview, it will either be too big or too small (depending on its contents).
I need to get the height of the textview with all the contents showing so I can adjust the view's size.
I know I could probably use a NSTextField as a label, but I need a NSTextView for its added functionality (specifically using the enclosing scrollview's rulerview).
Does anybody have any suggestions?
NSTextView generally will resize itself if its string over-runs the container width. I think this is because the contained cell has a default behavior for text over-run, called "Line Wrap" or something. My gut feeling is you could just ask the TextView for it's height after it's been loaded and adjust the containing view accordingly, all without needing a layout manager. And obviously make sure the auto-resizing mask is set (oh, you're doing this in IB so no worries there). I could be wrong, and I didn't do any tests... but yeah, you could try it! :P
Her's how I do it and it works well:
// Help text.
NSBundle* mainBundle = [NSBundle mainBundle];
NSString* path = [mainBundle pathForResource: #"category-analysis-help" ofType: #"rtf"];
NSAttributedString* text = [[NSAttributedString alloc] initWithPath: path documentAttributes: NULL];
[helpText setAttributedStringValue: text];
NSRect bounds = [text boundingRectWithSize: NSMakeSize(helpText.bounds.size.width, 0) options: NSStringDrawingUsesLineFragmentOrigin];
helpContentView.frame = NSMakeRect(0, 0, helpText.bounds.size.width + 20, bounds.size.height + 20);
helpContentView is just a container for helpText to add some marging around the text. helpText resizes with its container.
It should be obvious that for the correct height a fixed width is necessary, since the height depends on what fits on the lines.
If you want to omit the scroll view entirely (e.g., make a text view that is attached to another superview and sizes itself to fit its text), you might take a look at NSText. It is, AFAICT, basically a NSTextView without the superview (scroll view parts), and can automagically resize itself.