Implement Delegate Method on NSTextField - objective-c

I am attempting to implement a delegate method on NSTextField as described in this article from Apple. My goal is for the NSTextField to accept carriage returns and tabs. I have read elsewhere (including the linked article) that NSTextView is a better choice. However, I am working within a multiplatform framework that lacks support for NSTextView, and NSTextField will do the job if I can get it to accept carriage returns.
Based on the article, here is my code:
#interface MyTextFieldSubclass : NSTextField
{}
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;
#end
#implementation MyTextFieldSubclass
- (BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector
{
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == #selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
#end
Additionally, in the Identity Inspector of the text field, I have changed the class name from the default NSTextField to my class name. However, when I run my program, the delegate method never gets called. Is there something else I have to do to set this up in Interface Builder?

There are a few parts of the documentation you linked which is pertinent that I think may have been neglected.
I've copied a few of the lines below:
Should you decide to keep using NSTextField, allowing the tab key and/or allowing enter and return keys for line-breaks can be achieved by implementing the following delegate method:
(BOOL)control:(NSControl*)control textView:(NSTextView*)textView doCommandBySelector:(SEL)commandSelector;
Note: When implementing this delegate method in your own object you should set your object up as the "delegate" for this NSTextField.
I've bolded a few of the callouts which I think might have been missed.
This method is within the NSControlTextEditingDelegate protocol within NSControl.h. As such it should be implemented by a class which implements the NSControlTextEditingDelegate (i.e. NSTextFieldDelegate)
One common way of doing this is to have the ViewController "holding" the NSTextField be the NSTextFieldDelegate.
Here's a very simple example using the sample code from Apple you linked:
ViewController.h
#import <Cocoa/Cocoa.h>
#interface ViewController : NSViewController <NSTextFieldDelegate>
#end
ViewController.m
#import "ViewController.h"
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)setRepresentedObject:(id)representedObject {
[super setRepresentedObject:representedObject];
// Update the view, if already loaded.
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == #selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
#end
Then set your NSTextField's delegate to the ViewController
No need to add a custom subclass.
Alternatively you could probably make the custom text field subclass its own delegate. Something along these lines:
#import "MyTextFieldSubclass.h"
#interface MyTextFieldSubclass() <NSTextFieldDelegate>
#end
#implementation MyTextFieldSubclass
- (instancetype)init {
self = [super init];
if (self) {
self.delegate = self;
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super initWithCoder:coder];
if (self) {
self.delegate = self;
}
return self;
}
- (instancetype)initWithFrame:(NSRect)frameRect {
self = [super initWithFrame:frameRect];
if (self) {
self.delegate = self;
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
}
- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector {
BOOL result = NO;
if (commandSelector == #selector(insertNewline:))
{
// new line action:
// always insert a line-break character and don’t cause the receiver to end editing
[textView insertNewlineIgnoringFieldEditor:self];
result = YES;
}
else if (commandSelector == #selector(insertTab:))
{
// tab action:
// always insert a tab character and don’t cause the receiver to end editing
[textView insertTabIgnoringFieldEditor:self];
result = YES;
}
return result;
}
#end

Related

Opening a sheet on 10.9

In one of my controllers I'd like to open a modal sheet on 10.9.
This is the implementation code for the first controller:
#import "ABSAdvancedPreferencesViewController.h"
#import "ABSUnsyncableWindowController.h"
#interface ABSAdvancedPreferencesViewController ()
#property (strong, nonatomic) ABSUnsyncableWindowController *unsyncableWindowController;
#end
#implementation ABSAdvancedPreferencesViewController
- (id)init {
return [super initWithNibName:#"ABSAdvancedPreferencesViewController" bundle:nil];
}
- (IBAction)showUnsyncableSheet:(id)sender {
if (self.unsyncableWindowController == nil) {
self.unsyncableWindowController = [ABSUnsyncableWindowController new];
}
[self.view.window beginSheet:[self.unsyncableWindowController window] completionHandler:^(NSModalResponse returnCode) {
CLS_LOG(#"Table dismissed");
}];
}
When I execute the linked IBAction nothing happens. The NSWindowController subclass that should should show the modal has a XIB, Visible at launch is deactivated and window is already an outlet.
Debugging I saw that the window parameter is nil here, presumably because I call new in the previous controller:
#implementation ABSUnsyncableWindowController
- (id)initWithWindow:(NSWindow *)window {
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
return self;
}
Is there something else that I can check to show the modal sheet?
your ABSAdvancedPreferencesViewController initmethod is missing assigning the super result to self. Also, why don't you alloc/init the sheet controller as expected?
self.unsyncableWindowController = [[ABSUnsyncableWindowController alloc] initWithWindowNibName:#"XIBNAME"];
That should alloc the window controller and you should be able to access its window for display.

UISplitViewController delegate in a singleton

I did a lot of research on UISplitView and was not able to find a way to control a Split View when the Master and the Detail has a view that changes.
Then I found a way to manage it with a singleton class that is the delegate.
My problem is that i'm not sure if it's the right way to go. I'm concerned about reusability and memory managment. Also I have a feeling that it's aginst Apple guidelines to make delegates in singletons.
This is what I have (and it's actually working):
// SharedSplitViewDelegate.h
/* In the detail view controllers:
// in the initial detail view controller
- (void)awakeFromNib
{
[super awakeFromNib];
// needs to be here, otherwise if it's booted in portrait the button is not set
self.splitViewController.delegate = [SharedSplitViewDelegate initSharedSplitViewDelegate];
}
// shared between all detail view controllers
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation
{
SharedSplitViewDelegate *rotationHandler = [SharedSplitViewDelegate initSharedSplitViewDelegate];
[self.toolbar setItems:[rotationHandler processButtonArray:self.toolbar.items] animated:YES];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
*/
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#interface SharedSplitViewDelegate : NSObject <UISplitViewControllerDelegate>
+ (id)initSharedSplitViewDelegate; // returns the singleton class instance
- (NSArray *)processButtonArray:(NSArray *)array; // Adds and removes the button from the toolbar array. Returns the modified array.
#end
Now the implementation:
// SharedSplitViewDelegate.m
#import "SharedSplitViewDelegate.h"
#interface SharedSplitViewDelegate()
#property (nonatomic, strong) UIBarButtonItem *button;
#property (nonatomic, strong) UIBarButtonItem *cachedButton;
#end
#implementation SharedSplitViewDelegate
#synthesize button = _button;
#synthesize cachedButton = _cachedButton;
#pragma mark - Singleton class definition
static id sharedSplitViewDelegate = nil;
+ (void)initialize
{
if (self == [SharedSplitViewDelegate class]) {
sharedSplitViewDelegate = [[self alloc] init];
}
}
+ (id)initSharedSplitViewDelegate {
return sharedSplitViewDelegate;
}
#pragma mark - Split view delegate methods
- (BOOL)splitViewController:(UISplitViewController *)svc
shouldHideViewController:(UIViewController *)vc
inOrientation:(UIInterfaceOrientation)orientation
{
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
return NO;
} else {
return YES;
}
}
- (void)splitViewController:(UISplitViewController *)svc
willHideViewController:(UIViewController *)aViewController
withBarButtonItem:(UIBarButtonItem *)barButtonItem
forPopoverController:(UIPopoverController *)pc
{
barButtonItem.title = #"Browse";
self.button = barButtonItem;
}
- (void)splitViewController:(UISplitViewController *)svc
willShowViewController:(UIViewController *)aViewController
invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
self.button = nil;
}
#pragma mark - Utility methods
- (void)setButton:(UIBarButtonItem *)button
{
if (button != _button) {
_button = button;
}
if (button != nil) {
self.cachedButton = button;
}
}
- (NSArray *)processButtonArray:(NSArray *)array
{
NSMutableArray *processedArray = [array mutableCopy];
if (self.button != nil && ![processedArray containsObject:self.button]) {
[processedArray insertObject:self.button atIndex:0];
} else if (self.button == nil && [processedArray containsObject:self.cachedButton]) {
[processedArray removeObjectAtIndex:0];
}
return [processedArray copy];
}
#end
This code is free to use and modify for everyone that would find it viable in their project :).
I'm new to StackOverflow (even though I've lurked for a couple months without an account) so every critique is warmly welcomed.
IMHO, every design pattern, architecture, is 'good' if it fits the 'problem' you have to solve (and fits your personal preferences for code organisation)
What's your problem ?
Why do you need this object ?
Could this singleton UISplitViewDelegate be your
UIApplicationDelegate ? (Keep it Simple ;-)
further discussion =>
If you UIApplicationDelegate is a mess, rather than creating sub-object, a scheme I've been using recently to organize my code : use categories and class extensions
Example :
If my ViewController class handles complex tasks whose code can be separated in groups.
let's say :
sound
core data
location-aware,
I create a category for each of these
UIViewController+soundManager
UIViewController+dataProvider
UIViewController+locationManager.
(in same file with several #interface #implementation, or in different files => i use several files)
Then along with each category I write a class-extension for properties this particular category needs.
Last time I solved this by subclassing the UISplitViewController and used it as his own delegate.

How to call a function when button from datasource clicked ? Objective-C

My problem seems simple but i can't get the answer. I have a UiSwitch created in my TableItemCell subclass and I want him to call a function (dismiss in my case) from my tableviewcontroller that is instatiate.
How do I access this function from my subclass of TableItemCell ?
This is my code :
#implementation CCSettingsTableItemCell
#synthesize idSetting;
//Overriding cell
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString*)identifier {
if ((self = [super initWithStyle:UITableViewCellStyleValue2 reuseIdentifier:identifier])) {
switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
self.accessoryView = switchView;
[switchView addTarget:self action:#selector(switchChanged:) forControlEvents:UIControlEventValueChanged];
[switchView release];
}
return self;
}
- (void) switchChanged:(id)sender {
UISwitch* switchControl = sender;
/////////////////////////////////////////////////////////
//Here i want to call my function from my main controller
}
And my main controller code is as this :
#implementation SettingController
///////////////////////////////////////////////////////////////////////////////////////////////////
// private
// This is the function i want to call
- (void)dismiss {
[self dismissModalViewControllerAnimated:YES];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// NSObject
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
random code;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// TTTableViewController
- (void)loadView {
random code;
}
- (void)createModel {
self.dataSource = [SettingControllerDataSource viewDataSource];
// If dataSource nil, show an empty Message
if (self.dataSource == nil) {
[self showEmpty:YES];
}
}
#end
Clarification : the datasource adds object of my customcell type CCSettingsTableItem
Any help or hints would be great !
You can use #protocol which declares your "dismiss" method which is implemented by your SettingController. Conform SettingController class to CCSettingsTableItemCell.
Otherwise create an object of SettingController in the .h file of CCSettingsTableItemCell. Use this object to call "dismiss" method.

UIScrollView implementing a delegate

I have a view controller that has three scroll views as subviews. I would like to have the child scrolling views inform the parent view controller whenever the page has changed.
I thought I would do this by setting up a delegation pattern. My child (a subclass of UIScrollView) sets it up in the header file:
#interface TriptychScrollView : UIScrollView <UIScrollViewDelegate> {
id delegate;
}
- (id)delegate;
- (void)setDelegate:(id)newDelegate;
#end
#protocol RCScrollDidChange
#optional
- (void)scrollView:(TriptychScrollView *)scrollView imageDidChange:(NSNumber *)index;
#end
The source file has the delegate accessor methods:
- (id)delegate {
return delegate;
}
- (void)setDelegate:(id)newDelegate {
delegate = newDelegate;
}
Unfortunately, this means my setting the delegate to self call is being ignored:
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setDelegate:self];
}
return self;
}
.. and therefore the two methods scrollViewDidScroll: and scrollViewDidEndDecelerating: are not getting called! This means I've effectively lost control of my scrolling views.
Am I doing this incorrectly? Or is there a better way for the child subviews to send messages back to their parents?
I may not understand the whole problem, but I dont see a reason for implementing your own subclasses of UIScrollView, and if you are insistent on doing so, dont shadow it's .delegate property or make sure and call super.
I would approach this by doing the following:
assuming all of these scrollviews are contained by some other view;
UIScrollView *a = ....
a.delegate = self;
a.tag = 1;
UIScrollView *b = ....
b.delegate = self;
b.tag = 2;
UIScrollView *c = ....
c.delegate = self;
c.tag = 3
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
if (scrollView.tag == 1)
//handle a
else if (scrollView.tag == 2)
//handle b
else if (scrollView.tag == 3)
//handle c
}

Return Key Text Field? - iPhone SDK

I've been searching the web for a really long time, and I can't get this to work. On my text field I have, when I click on it and then press done or return it won't go away. I've done all the steps for every single tutorial but I still can't get it to work. I'm on firmware 3.1.2, but anyway here is the code in my ViewController.m:
- (void)viewDidLoad {
[super viewDidLoad];
blah.delegate = self;
blah.returnKeyType = UIReturnKeyDone;
}
- (BOOL)blahShouldReturn:(UITextField *)blah{
[blah resignFirstResponder];
return YES;
}
viewcontroller.h:
#interface BlahViewController : UIViewController <UITextFieldDelegate> {
IBOutlet UITextField *blah;
}
These are just cut outs from the files. Anyway can anyone help me? I can't get rid of the keyboard when I click on it...
Thanks,
Kevin
I'm confused. Are you actually expecting a method called blahShouldReturn: to get called when you press the Return button? If you want to use the textFieldShouldReturn: delegate method, it has to be called textFieldShouldReturn:. You can use the UITextField parameter supplied with that method to determine which text field is sending the message. For example:
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
if (textField == blah) {
[textField resignFirstResponder];
} else if (textField == someOtherTextField) {
// Do something else
}
return YES;
}
.m file:
- (void)viewDidLoad {
[super viewDidLoad];
blah.delegate = self;
blah.returnKeyType = UIReturnKeyDone;
//Call hideKeyboard action when done button is clicked
[blah addTarget:self action:#selector(hideKeyboard) forControlEvents:UIControlEventEditingDidEndOnExit];
}
//Custom hide keyboard action
- (void)hideKeyboard {
[blah resignFirstResponder];
}
.h file:
//Dont need delegate for this method of hiding keyboard!
#interface BlahViewController : UIViewController {
IBOutlet UITextField *blah;
}
- (void)hideKeyboard;
#end