NSTextView placeholder text only visible when selected - objective-c

I'm currently experiencing a problem like the one described in this SO question (which does not currently have an accepted answer), in that my text fields' placeholder text is not visible unless the text field is selected.
I have subclassed NSTextField (code below):
#interface CustomTextField : NSTextField
#property (nonatomic, strong) IBInspectable NSImage *backgroundImage;
#end
#implementation CustomTextField
- (void)awakeFromNib
{
[self setDrawsBackground:NO];
}
- (void)drawRect:(NSRect)rect
{
NSImage *backgroundImage = self.backgroundImage;
[super drawRect:rect];
[self lockFocus];
[backgroundImage drawInRect:rect fromRect:rect operation:NSCompositeSourceOver fraction:1.0];
[self unlockFocus];
}
#end
I've set the class of my text field in Interface Builder to be CustomTextField, and set the placeholder text as shown below:
As can be seen in the screenshots below, the placeholder text is only visible if the text field is selected...
Text field one:
Text field two:
Does anyone have any idea on how I can make the placeholder text visible regardless of if the user has selected it? Thank you!

You are drawing your background image after the superclass has drawn its content. So, you are probably drawing over whatever the superclass has drawn, replacing it.
You should presumably draw your background image first, before calling through to super. Also, you shouldn't lock focus (or unlock it) in -drawRect:. The frameworks have already done that for you.
The reason why the placeholder shows up when your text field has focus is that you're actually seeing the field editor, not the text field at that point. The field editor is an instance of NSTextView ("view", not "field") that's inserted into the view hierarchy on top of the text field to handle text editing duties. So, when the text field has focus, your custom class's drawing it irrelevant.

Related

NSTextView auto-scroll

Suppose there is a window with has a NSTextView which contains enough text to trigger the scrollbars. When I resize the window, the textview is automatically scrolled so that the line which contains the cursor appears in the middle of the textview.
For example, this can also be seen in TextEdit in MacOS: paste bunch of text in it, scroll almost to the top [1], place cursor into the first visible line and resize the window. Now the view should scroll its content so that the cursor lands in the middle of the view.
My question is, how do I turn off this behavior? That is, I would like the textview to never automatically scroll the cursor to the middle when the window gets resized..?
[1] The actual scroll position at which the said behavior happens may require some trial-and-error, as I was unable to find out a pattern at which this happens. In my testing it happened when the scrollbar is at 10% - 30% position of the total height (from the top).
You can do the tweak like this below:-
Create Custom Class of NSTextView and implement one delegate method for textview resizing and one method when click on textview. Refer below:-
.h file
#interface textView : NSTextView
#end
.m file
#import "textView.h"
#implementation textView
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
//Below delegate method which will call when resize the textview. So just set your text view to be non editable.
- (void)viewDidEndLiveResize
{
[self setEditable:NO];
[self setSelectable:NO];
}
//Now when you click on the textview below method will called.
-(void)mouseDown:(NSEvent*) theEvent
{
[super mouseDown:theEvent];
[self setEditable:YES];
[self setSelectable:YES];
}
-(void)keyDown:(NSEvent *)theEvent
{
[super keyDown:theEvent];
[self setEditable:YES];
[self setSelectable:YES];
}
#end
Edit:-
Also, mention the custom class name in interface builder inside textview -> Custom Class

How I can get the value of my UITextField?

how I can get the value of my UITextField ? When I declare my UITextField in the Storyboard, I know but like this, I don't know.
(sorry for my English, I'm French)
Thank you in advance for your answer.
//Ajout d'un Text Field
CGRect rectTF = CGRectMake(10,70,100,20); // Définition d'un rectangle
UITextField *articleSaisi = [[UITextField alloc] initWithFrame:rectTF];
articleSaisi.borderStyle = UITextBorderStyleLine;
articleSaisi.placeholder = #"Article";
[self.view addSubview: articleSaisi];
I am not quite sure whether you want to access the value or you want to associate your UI element to your code:
Try this if you are saying that you want to access the text value.
atricleSaisi.text
Try to control drag the UI element to either your corresponding .h or .m file so that it can create IBOutlet for you if you are saying that you want to connect your UI element to your code.
Highly recommend you go check the documentation.
if you want to know when the user pressed return, as per your comment, then you should create a UITextViewDelegate for your text view and define its textFieldShouldReturn method:
Discussion
The text field calls this method whenever the user taps the return button. You can use this method to implement any custom behavior when the button is tapped.
Also, give a look at textFieldDidEndEditing, which is called whenever there is a focus change and you should define to correctly handle user input.
Old answer:
If I do not understand you incorrectly, you want to create a UITextField programmatically (i.e., not through a Storyboard).
In this case, you should put you initialisation code inside the viewDidLoad method of your view controller and make sure that you define a property for the text field instead of a local variable:
#interface MyViewController : UIViewController
....
#property (nonatomic, strong) UITextField* articleSaisi;
...
#end
- (void)viewDidLoad {
CGRect rectTF = CGRectMake(10,70,100,20); // Définition d'un rectangle
self.articleSaisi = [[UITextField alloc] initWithFrame:rectTF];
self.articleSaisi.borderStyle = UITextBorderStyleLine;
self.articleSaisi.placeholder = #"Article";
[self.view addSubview:self.articleSaisi];
}
If you do so, you can access the text field value from any other method in the view controller through its text property:
self.articleSaisi.text

NSTextView not properly resizing with auto layout

I have a simple layout, which consists of NSView and its subview NSTextView. NSTextView is programmatically filled with some text that spawns multiple lines. I tie everything together using auto-layout (all done programmatically). However, when everything is displayed NSTextView is cut off, only one line is showing.
After searching the web, the best answer I could find was:
Using Autolayout with expanding NSTextViews
However, this only works if I manually change the text in NSTextView after everything is displayed (which is not really my use case). The views are readjusted and the whole NSTextView is displayed.
I am trying to figure out when NSViewController is done with laying out subviews so that I could call invalidateIntrinsicContentSize on the NSTextView. The equivalent of viewDidLayoutSubviews in UIViewController.
Nothing I tried worked so far. I attempted calling invalidateIntrinsicContentSize for NSTextView:
At the end of loadView
After I filled NSTextView with my text
Is there a better way to achieve this?
After further research, found the answer:
Create custom NSView subclass that contains NSTextView
In NSView subclass override layout method that calls invalidateIntrinsicContentSize
Also check out this link that explains subtleties of auto layout and intrinsic content size (among many other things):
http://www.objc.io/issue-3/advanced-auto-layout-toolbox.html
Sample code:
#interface MyView : NSView
#property MyTextView *textView;
#end
#implementation MyView
// init & create content & set constraints
-(void) layout {
[super layout];
[self.textView invalidateIntrinsicContentSize];
}
#end
Implementation of MyTextView:
#implementation MyTextView
- (NSSize) intrinsicContentSize {
NSTextContainer* textContainer = [self textContainer];
NSLayoutManager* layoutManager = [self layoutManager];
[layoutManager ensureLayoutForTextContainer: textContainer];
return [layoutManager usedRectForTextContainer: textContainer].size;
}
- (void) didChangeText {
[super didChangeText];
[self invalidateIntrinsicContentSize];
}
#end

How to programmatically change label in UIView created in a nib file?

I have trouble changing the text in a label programmatically.
When I run the following code, NSLog does display "Setting myLabel to = Hello World!", but the label on the screen is not changed.
UIViewOverlay *overlayWindow;
overlayWindow = [[[NSBundle mainBundle] loadNibNamed:#"UIViewOverlay" owner:self options:nil] objectAtIndex:0];
[self addSubview:overlayWindow];
[overlayWindow setMyLabel:#"Hello World!"];
My NIB file has a 300x300 window with some labels and buttons.
There is a label, which is connected to myLabel in the outlet. The UIView does display, just that the text cannot be changed programmatically.
UIViewOverlay.h
#import <UIKit/UIKit.h>
#interface UIViewOverlay : UIView {
IBOutlet UILabel *myLabel;
}
- (void)setMyLabel:(NSString *) label;
#end
UIViewOverlay.m
#import "UIViewOverlay.h"
#implementation UIViewOverlay
- (void)setMyLabel:(NSString *) label {
myLabel.text = label; // THIS LINE IS NOT WORKING! :-(
NSLog(#"Setting myLabel to = %#", label); // This line is working.
}
#end
Thanks in advance..
You are using an incorrect accessor name for your method to set the label string.
In cocoa, setFoo is the method by which an instance variable called foo is assigned. This isn't just a convention, many areas of functionality depend on it, for example the use of properties, key value coding etc.
In your code, your label is called myLabel. Your method to set the text of that label is called setMyLabel. Either this is causing your outlet to not be connected when the nib is loaded, as the runtime may be trying to use that method to assign the label to your instance variable in the first place, or all of the above has no effect and you have just not connected your outlet.
Make sure you have an object in your code for the label, like this example:
IBOutlet UILabel* aLabel;
And in interface builder (you may have already done this): Connect the aLabel (or whatever name you use) outlet to the actual label. This can be done by control clicking and dragging from the File’s Owner object, in the document window, to the label, in the view. A small, gray window will appear with at least two options, one will be the aLabel defined earlier, and the other will be the view (this is a default outlet required for viewcontrollers, it will have a dash to indicate it is already connected to something). Click on the aLabel option to select it. (I'll be honest, without my mac in front of me I copied most of this paragraph's instructions from this link.)
You have an object called aLabel that you can now treat like any other variable. If you want to change the text value, try this:
aLable.text = #"some text";
Maybe you did not make the connection. Can you try the following?
#import "UIViewOverlay.h"
#implementation UIViewOverlay
- (void)showMyLabel:(NSString *) label {
NSLog(#"My current label contents (myLabel) = %#", myLabel.text); // ** changed to myLabel.text
}
#end
If you aren't able to print the original value, then you aren't connected.
I hope that helps.

UITableViewCell selection over UITextView

I get stuck with my cell selection. I have custom cell with UITextView object embedded in. It is not editable, not scrollable, with user interaction enabled. And I can not select cell over this view.
I've tried the same thing but with UITextField object and selection works.
Any ideas how to make it work?
This is the test project. There is just one table view with one cell. There is one text view and one text field in this cell. You can select the cell over the text field and can not over the text view.
Turn off "User Interaction Enabled" on your text view and the selection will work fine (MainStoryboard.storyboard->Table View Controller->Table View->Table View Section->Custom Table View Cell->Text View). Allowing user interaction on the text view is capturing the taps before the table view cell gets them. Since your text view isn't scrollable or editable, you don't want user interaction enabled.
Just to expound a bit on John Stephen's answer (which is the best as far as I can tell).
I've tried this by subclassing UITextView and overriding canBecomeFirstResponder:
#interface MyTextView : UITextView
#end
#implementation MyTextView
- (BOOL)canBecomeFirstResponder {
return NO;
}
#end
I've also set editable, selectable, and scrollEnabled to NO:
MyTextView *textView = [MyTextView new];
textView.editable = NO;
textView.selectable = NO;
textView.scrollEnabled = NO;
None of these worked. :(
The equivalent of John's answer for Storyboards for programmatic solution is:
textView.isUserInteractionEnabled = false