Creating a NSTextView programmatically? - objective-c

I'm can't seem to build a working NSTextView programmatically. Starting from a new project template I have this code (mostly coming from Apple's "Text System User Interface Layer Programming Guide"):
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSString *string = #"Here's to the ones who see things different.";
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:string];
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
[textStorage addLayoutManager:layoutManager];
NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(300, 300)];
[layoutManager addTextContainer:textContainer];
NSTextView *textView = [[NSTextView alloc] initWithFrame:NSMakeRect(50, 50, 300, 300)
textContainer:textContainer];
[textContainer setWidthTracksTextView:YES];
[textView setVerticallyResizable:YES];
[textView setHorizontallyResizable:NO];
[textView setAutoresizingMask:NSViewWidthSizable];
[[self.window contentView] addSubview:textView];
}
Running the app, a window opens with a white square in it that's supposed to be the NSTextView, but there's no text, and there's no way to interact with the text view. Moreover, I tried adding text to the text view in code after the view is created using [textView setString:#"Some string."], but that doesn't work either. (Incidentally, I've also tried putting it in a NSScrollView first, in case that somehow was the issue, but that didn't help.)
I can't for the life of me figure out what the problem is. What am I missing?

This should work, although I tried it out myself and was unable to edit too. It could possibly be a bug now, but I'm not 100% sure. If you need to use the containers and layout managers just use the one that comes from the initWithFrame: method (without the text container).
NSTextView *textView = [[NSTextView alloc] initWithFrame:NSMakeRect(50, 50, 300, 300)];
NSLayoutManager *layoutManager = textView.layoutManager;
NSTextContainer *textContainer = textView.textContainer;
NSTextStorage *textStorage = textView.textStorage;
//Do your edits here

To expand on TheAmateurProgrammer's answer:
If you need to use the containers and layout managers just use the one that comes from the initWithFrame: method (without the text container)
With iOS, needed to create the text storage, layout manager, and text container for UITextView to work as intended. For NSTextView to function, needed to not create those. Had same results as OP until this change--the view was located accurately, but didn't display text or show insertion point or invoke delegate methods--and then the text view worked properly.

Related

UITextView renders custom font incorrectly in iOS 7

My app uses a UITextView to input Syriac text (Estrangelo font), but UITextView renders some letters incorrectly like this:
I tested it with a UILabel and a UITextView. UILabel displays it correctly, but UITextView incorrectly displays the top dots and moves them to the bottom (see the above result).
This problem only occurs in iOS 7 and does not occur in iOS 6. Please tell me if there's any way to fix the problem.
This is my test code
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
label.center = CGPointMake(self.view.center.x, self.view.center.y-40);
label.font = [UIFont fontWithName:#"East Syriac Adiabene" size:24];
label.text = #"ܩ̈ ܡ̄ ܬ̇ ܒ̃";
[self.view addSubview:label];
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
textView.center = CGPointMake(self.view.center.x, self.view.center.y+40);
textView.font = [UIFont fontWithName:#"East Syriac Adiabene" size:24];
textView.text = #"ܩ̈ ܡ̄ ܬ̇ ܒ̃";
[self.view addSubview:textView];
I debugged this issue a little, and it seems to be a bug in the way NSLayoutManager layouts the text. As other answers pointed out, UITextView is build around TextKit since iOS7, and thus uses NSLayoutManager internally to layout text. UILabel uses Core Text to layout text directly. Both eventually use Core Text to render the glyphs.
You should open a bug report with Apple and post the number so people can duplicate it. The issue has not been fixed so far in iOS7.1 betas.
As a workaround, you can replace UITextView with other Core Text alternative editors, which layout and render directly with Core Text, where the issue does not exist.
I tested SECoreTextView, and it shows the text correctly. It implements a similar API to UITextView but internally uses Core Text.
Here is how it looks after swapping UITextView with SECoreTextView:
SETextView *textView = [[SETextView alloc] initWithFrame:CGRectMake(0, 0, 100, 40)];
textView.center = CGPointMake(self.view.center.x, self.view.center.y+40);
textView.font = [UIFont fontWithDescriptor:desc size:24];
textView.text = #"ܩ̈ ܡ̄ ܬ̇ ܒ̃";
textView.textColor = [UIColor blackColor];
textView.backgroundColor = [UIColor whiteColor];
textView.editable = YES;
[self.view addSubview:textView];
Some days ago, I had the same problem as yours in iOS 7 (but the font was different).
I set the FONT after setting the TEXT and it worked for me. So for your case:
textView.text = #"ܩ̈ ܡ̄ ܬ̇ ܒ̃"; // Setting the text.
textView.font = [UIFont fontWithName:#"East Syriac Adiabene" size:24]; // Setting the font and it's size.
It may seem silly, but it worked for me.
Also see this question/answer. There are many tips for using custom fonts with UITextView, which may be helpful for you.
EDIT :
iOS 7 also introduced a new selectable property on the UITextView for enabling text selection. So make sure you have done the following:
self.textField.editable = YES;
self.textField.selectable = ON;

NSTextField bug with FocusRingType: None

There seems to be a bug in NSTextField. When the application launches it all draws correctly. But as soon as I click in the textField the view gets all messed up. To more specific, whenever I type drawRect: gets called but with a smaller rect causing all the problems.
When I select the text it draws correctly again. The only solution is to set the FocusRingType to visible (example: NSFocusRingTypeDefault). But I would like to have it without the ring. Is this possible?
Here is the code I am using:
-(id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect];
if(self)
{
// Add a label
NSTextField *textField = [[NSTextField alloc] initWithFrame:CGRectMake(0, 0, frameRect.size.width, frameRect.size.height)];
[[textField cell] setPlaceholderString:#"URL or search term..."];
[textField setTextColor:[NSColor greyColor]];
[textField setBackgroundColor:[NSColor clearColor]];
[textField setFont:[NSFont fontWithName:#"Open Sans" size:20]];
[textField setDrawsBackground:FALSE];
[textField setBordered:FALSE];
[textField setFocusRingType:NSFocusRingTypeNone];
[self addSubview:textField];
}
return self;
}
-(void)drawRect:(NSRect)dirtyRect
{
NSInteger borderWdith = 2;
// Create the path to the button
NSBezierPath *aPath = [NSBezierPath bezierPathWithRoundedRect:CGRectMake(borderWdith, borderWdith,
dirtyRect.size.width-(borderWdith*2),
dirtyRect.size.height-(borderWdith*2))
xRadius:3 yRadius:3];
// Fill the button with white
[[NSColor whiteColor] set];
[aPath fill];
}
Tricks like setting editable to TRUE/FALSE in drawRect did not work. Also setting to different focusRingTypes in the method failed.
Okay, so I managed to sort of fix this drawing problem. In the view init method I set a global CGRect variable which was set to the initial frame.
Each time -(void)drawRect:(CGRect)dirtyRect was called, I overwrote dirtyRect with the global variable.
Hope this helps anyone. I guess this isn't the best solution, but works :)

addSubview not showing up in NSView Objective C Cocoa

When I addSubview: nothing shows up. I set text and color to see it. Also if I manually add the view to the custom view in the UI bulder in xcode it shows up just fine with the text and color.
.m file
- (void)displayString:(NSString *)title {
NSRect frame = NSMakeRect(10, 10, 200, 17);
NSTextfield *newfield = [[NSTextField alloc] initWithFrame:frame];
[newfield setBezeled:NO];
[newfield setDrawsBackground:NO];
[newfield setEditable:NO];
[newfield setSelectable:NO];
[newfield setStringValue:title];
[newfield setTextColor:[NSColor blueColor]];
[test addSubview:newfield];
if([test.subviews containsObject:newfield]){
NSLog(#"view there"); // i get this message
}
if([newfield isHidden]){
NSLog(#"view hidden"); //i dont get this message
}
NSLog(#"view set");
}
test is a NSView (Custom view is what xcode calls it) that I have properly linked in.
So when I create the text field and add it to the NSView manually and then run that same code by adding text and color all works fine, this issue arrises when I try programmatically setting the view. Also I made sure it wasn't my creating of the view, as I have tried creating the view in the builder and not placing it in the NSView and then trying addSubview: but that also does not work. Let me know if you need more code.
DEVELOPMENT:
If the nsview (custom view) has an element already in it (manually added and can be anything) and I add the text field it works (I get both views in the nsview)? The subview is tested for and there, just cant see it.
You have to call initWithFrame: instead of just init
- (void)displayString:(NSString *)title {
NSRect frame = NSMakeRect(10, 10, 200, 200);
NSTextField *newfield = [[NSTextField alloc] initWithFrame:frame];
[newfield setStringValue:title];
[newfield setTextColor:[NSColor blueColor]];
[test addSubview:newfield];
NSLog(#"view set");
}
What type of view is test? Also you need to do a:
newfield.frame = CGRectMake(x,y,width,height)
in order to specify the look of the view
Turns out I set the view too early. I was under the impression that whatever you do to the view after its been set will be reflected on the view, but that seemed to be the issue. After altering the view to be exactly they way I want then set the view of the NSStatuditem.
so I get
[newfield setStringValue:title];
[newfield setTextColor:[NSColor blueColor]];
[test addSubview:newfield];
[statusItem setView:test];//this is the key, setting it after he changes.

How to make a UIActivityIndicatorView in a UIToolbar tappable

I'm using this code to insert an UIActivityIndicatorView to my toolbar
-(void)addActivityIndicatorToToolbar {
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
activityIndicator.userInteractionEnabled = YES;
activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhite;
[activityIndicator startAnimating];
UIBarButtonItem *barButton = [[UIBarButtonItem alloc] initWithCustomView:activityIndicator];
barButton.target = self;
barButton.action = #selector(playButtonPressed:);
NSMutableArray *toolbarItemsMutable = [self.toolbar.items mutableCopy];
[toolbarItemsMutable replaceObjectAtIndex:0 withObject:barButton];
self.toolbar.items = toolbarItemsMutable;
}
However, when I tap the UIActivityIndicatorView the action (playButtonPressed) is not performed.
How can I correct this?
It seems more likely that you want a button with an activity indicator inside it. You can do this by creating a button with a custom view as described in this post. Then you can set the action of this button as normal, and you'll probably want to retain a reference to the activity indicator to start and stop it.
I ended up implementing a poor man's solution by adding an extra view on top of the activityIndicatorView with a gestureRecognizer.
This is a quite old question but why don't you directly add a UITapGestureRecognizer instance to your UIActivityIndicatorView instance ? (works fine on iOS 8.2, I didn't test yet on previous versions).

Programmatically create NSWindow with specific size fails

I'm trying to programmatically create a borderless NSWindow close to the mouse cursor and with the size of the image it should display. However I always get a much larger window (each side is about three times the size it should be)! I double-checked that my imageObj has the correct size and that all the NSSize and NSRect structures are created with correct values. The commented-out line doesn't work either (however which would be correct?)
I already searched through this site but I couldn't find any similar issue... What am I doing wrong?
Here is my code:
NSString* imageName = [[NSBundle mainBundle] pathForResource:#"os_unknown" ofType:#"icns"];
NSImage* imageObj = [[NSImage alloc] initWithContentsOfFile:imageName];
[view setImage:imageObj];
NSSize s = [imageObj size];
NSPoint p = [NSEvent mouseLocation];
NSRect r = [NSWindow frameRectForContentRect:NSMakeRect(p.x, p.y, s.width, s.height)
styleMask:NSBorderlessWindowMask];
//NSRect r = {p, s};
if (win) [win release];
win = [[NSWindow alloc] initWithContentRect:r
styleMask:NSBorderlessWindowMask
backing:NSBackingStoreBuffered
defer:false];
[win setLevel:kCGUtilityWindowLevel];
[view setBounds:NSMakeRect(0, 0, s.width, s.height)];
[[win contentView] addSubview:view];
[win orderFront:sender];
Here *win is an NSWindow and *view is an NSImageView. Both are declared on top of my source file.
I'm running Snow Leopard so any strictly >10.6.8 code won't work (already tried something).
Thank you in advance for any help.
Solved! The NSImageView *view was created in Interface Builder. The size I got was the size set in there, no matter what code I used to resize it. By creating the NSImageView programmatically everything works great!
Special thanks to rdelmar who made me think and get the solution!