I wanna setup a basic PFQuery Table View Controller. I wanna display the usernames from my User class. I've added the User class to query on, but it gives this error message: *Property 'className' not found on object of type 'InboxViewController '
I don't understand why happens this, because the user class exists, it appears in the data browser. I've tried to make a property in the .h file, but it was unsuccessful.
#import "InboxViewController.h"
#import <Parse/Parse.h>
#import "LoginViewController.m"
#interface InboxViewController ()
#end
#implementation InboxViewController
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
// Custom the table
// The className to query on
self.className = #"User";
// The key of the PFObject to display in the label of the default cell style
self.textKey = #"username";
// Uncomment the following line to specify the key of a PFFile on the PFObject to display in the imageView of the default cell style
// self.imageKey = #"image";
// Whether the built-in pull-to-refresh is enabled
self.pullToRefreshEnabled = YES;
// Whether the built-in pagination is enabled
self.paginationEnabled = YES;
// The number of objects to show per page
self.objectsPerPage = 25;
}
return self;
}
#pragma mark - UIViewController
- (void)viewDidLoad
{
[super viewDidLoad];
PFUser *currentUser = [PFUser currentUser];
if (currentUser) {
NSLog(#"Current user: %#", currentUser.username);
} else {
[self performSegueWithIdentifier:#"showLogin" sender:self];
}
}
.h file
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface InboxViewController : PFQueryTableViewController
- (IBAction)logout:(id)sender;
#end
OK, I just remembered.
The property you are looking for is parseClassName not className.
Change this and it should work.
Related
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
I am trying to update the contents of an NSTextView that is connected to myViewController as a referencing outlet to the Files Owner which is the subclass myViewController.
When I use an IBAction from a button, or use the viewDidLoad method of the controller, I can update the text fine. However, when I try run the method from another class (referred to in this example as anotherViewController), it runs the method, but the textview does not change.
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
self.outText.string = #"I am updated text! I also work!";
}
- (void)updateTextView:(NSString *)argText {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
- (void)updateTextViewWithoutArg {
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
#end
In anotherViewController.m , which has all the relevant imports, I call this:
myViewController *viewtask = [[myViewController alloc] init];
[viewtask updateTextViewWithoutArg];
Nothing happens. The method runs and logs that it should have updated, but no text updates. I have tried many different approaches, including textstorage and scrollrange methods, they all work the already working sections, but make no difference in the sections not working.
I've also tried just for fun:
myViewController *viewtask;
[viewtask updateTextViewWithoutArg];
Also using the instance variable _outText
Also using [self.outText setString:#"string"];
Also using [_outText setString:#"string"];
Again, they work but only in the already working sections.
This should be simple but isn't logical to me. In swift all I need to do is
self.outText.string = "I update whenever I'm called!"
Views you create in Interface Builder are lazily created, so if you access them before viewDidLoad is called they are nil.
If your case, calling
myViewController *viewtask = [[myViewController alloc] init];
does not cause the views to be created so when you call
[viewtask updateTextViewWithoutArg];
self.outText is nil.
You can see that this is what is happening by updating your code as below:
- (void)updateTextView:(NSString *)argText {
NSAssert(self.outText != nil, #"self.outText must not be nil");
self.outText.string = #"I don't make it to the NSTextView :(";
NSLog(#"Should have updated text view");
}
you should see the assert fire.
I appear to have found a solution by making myViewController a singleton class and using sharedInstance. For this particlar app, myViewController is a debug output window and will never need to be placed in another view.
I won't accept this answer yet, as it's not the best one I'm sure. There may still be a proper solution presented that allows finding the applicable myViewController instance, and modifying the outText property attached to it. Using this singleton makes subclassing tedious as I would have to make a new class for every instance if I wanted to be able to address say 10 View Controllers.
Anyway - the way I've been able to satisfy my simple requirement:
myViewController.h:
#import <Cocoa/Cocoa.h>
#import "anotherViewController.h"
#interface myViewController : NSViewController { }
#property (unsafe_unretained) IBOutlet NSTextView *outText;
#property (weak) IBOutlet NSButton *updateMeButton;
- (void)updateTextView:(NSString *)argText;
- (void)updateTextViewWithoutArg;
+ (id)sharedInstance;
#end
myViewController.m:
#import "myViewController.h"
#interface myViewController ()
#end
#implementation myViewController
static myViewController *sharedInstance = nil;
+ (myViewController *)sharedInstance {
static myViewController *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[myViewController alloc] init];
});
return sharedInstance;
}
- (void)viewDidLoad {
[super viewDidLoad];
sharedInstance = self;
}
- (void)viewDidUnload {
sharedInstance = nil;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.outText.string = #"I work successfully";
}
- (IBAction)updateMeButton:(id)sender {
sharedInstance.outText.string = #"Button Pressed";
}
- (void)updateTextView:(NSString *)argText {
sharedInstance.outText.string = argText;
}
- (void)updateTextViewWithoutArg {
sharedInstance.outText.string = #"I make it to the TextView now";
}
#end
Now when I use this code from within anotherViewController.m it updates the right instance:
[myViewController.sharedInstance updateTextView:#"Updating with this string"];
In my WhatToEditViewController, I created an NSString property called whattoedit. When the user presses a certain button, whattoedit gets assigned to a string.
#interface WhatToEditViewController : UIViewController
#property (nonatomic, strong) NSString * whattoedit;
#end
#import "WhatToEditViewController.h"
Implementation:
#implementation WhatToEditViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)TappedMorning:(id)sender {
self.whattoedit = #"Morning";
}
- (IBAction)TappedAfternoon:(id)sender {
self.whattoedit = #"Afternoon";
}
- (IBAction)TappedEvening:(id)sender {
self.whattoedit = #"Evening";
}
(I checked this part of my program works :/) Also when the user presses the button, a new view controller called SettingsTableViewController gets pushed in. What I want to do is set the title of the navigation bar of SettingsTableViewController as WhatToEditViewController's whattoedit property.
Here is SettingsTableViewController's implementation:
#import "SettingsTableViewController.h"
#import "WhatToEditViewController.h"
#interface SettingsTableViewController ()
#end
#implementation SettingsTableViewController
- (void)viewDidLoad {
[super viewDidLoad];
WhatToEditViewController * editMode = [[WhatToEditViewController alloc] init];
self.navigationController.navigationBar.topItem.title = editMode.whattoedit;
}
Oddly, the navigation bar title turns into (null). Should the navigationBar.title code be in the viewDidLoad?
It is because you're initializing new WhatToEditViewController at viewDidLoad in your SettingsTableViewController, so the value of whattoedit becomes NULL
The correct way is to create a instance variable in your SettingsTableViewController that will update the value of title:
So, when you click the button to push in SettingsTableViewController, you should update the value like this:
In your SettingsTableViewController.h:
#property (nonatomic, strong) NSString *titleValue;
In your WhatToEditViewController, when you push
- (IBAction)pushToSettingVC:(id)sender {
SettingsTableViewController *setting = [self.storyboard instantiateViewControllerWithIdentifier:#"SettingsTableViewController"];
setting.titleValue = YOUR_NEW_TITLE_VALUE_HERE;
[self.navigationController pushViewController:setting animated:YES];
}
and in your SettingsTableViewController.m viewDidLoad
- (void)viewDidLoad {
[super viewDidLoad];
self.title = self.titleValue
}
I have a couple of views, one being the SettingsMenu the other being Game .
I have initialised backgroundMusic from within Game. When there is a change in SettingsMenu, I'd like it to run backgroundMusicStop in settingsMenu, however backgroundMusicStop is part of my Game class.
Game.h
-(void)backgroundMusicStop;
Game.m
-(void)backgroundMusicStop {
[backgroundMusic stop];
backgroundMusic.currentTime = 0;
}
SettingsMenu.m
-(IBAction)musicOptionSwitch:(id)sender {
if (backgroundMusicPlay == YES) {
backgroundMusicPlay = NO;
[Game backgroundMusicStop];
}
}
I've looked into it and I don't understand how to fix it, I know I need to make the method accessible from all classes, but I am confused on how to do that, any help would be greatly appreciated.
The problem is in this line of code:
[Game backgroundMusicStop];
In your implementation, Game is a class name and you try to call an instance method on a class. There are 2 options to fix it:
Create a new Game instance
Make backgroundMusicStop a class method
To create a Game instance you would need something like:
Game myGame = [[Game alloc]init];
//depending on your implementation it could be different
Or if you choose for second option you will have to change
-(void)backgroundMusicStop;
to
+(void)backgroundMusicStop;
I would also suggest you to find some book/website to understand class and instance variables/methods better and more in-depth.
I think the best architecture for what you're trying to do would be:
create one instance of a Settings object and have it keep track of the
current state of each option you want the user to control
update those values from inside your SettingsMenu
have your Game listen for changes to the Settings values
The advantage in this is that your view controllers only communicate through changes to the data model (Settings) rather than having to know about each other. Your Settings object can either be a singleton or a normal object that you get from your app delegate.
First create the settings data model:
// Settings.h
#import <Foundation/Foundation.h>
#interface Settings : NSObject
// A singleton so that the example stays simple
+ (instancetype)sharedSettings;
// YES/NO. Add other properties as needed.
#property (nonatomic, assign) BOOL musicShouldPlay;
#define MUSIC_SHOULD_PLAY #"musicShouldPlay"
#end
// Settings.m
#import "Settings.h"
#implementation Settings
// Guarantee only one instance
+ (instancetype)sharedSettings {
static dispatch_once_t onceToken;
static Settings *result = nil;
dispatch_once(&onceToken, ^{
result = [[Settings alloc] init];
});
return result;
}
- (instancetype)init {
self = [super init];
if (self) {
_musicShouldPlay = NO;
}
return self;
}
#end
The controller that makes the changes:
// SettingsViewController.m
#import "SettingsViewController.h"
#import "Settings.h"
#interface SettingsViewController ()
#property (strong, nonatomic) IBOutlet UISwitch *musicSwitch;
#end
#implementation SettingsViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Make the display consistent with previous settings
[self.musicSwitch setOn:[[Settings sharedSettings] musicShouldPlay]];
}
// I'm presenting this using a modal segue from a "Change settings" button on the main controller,
// so it has a "Done" button
- (IBAction)done:(id)sender {
[ self dismissViewControllerAnimated:YES completion:nil];
}
// Switch value changes are connected to this in the storyboard
- (IBAction)changePlayState:(id)sender {
[[Settings sharedSettings] setMusicShouldPlay:[self.musicSwitch isOn]];
}
#end
The controller that reacts to changes:
// ViewController.m
#import "ViewController.h"
#import "Settings.h"
#implementation ViewController
// Since this is the primary controller, I'll have it listen for its lifetime.
- (void)viewDidLoad {
[super viewDidLoad];
[[Settings sharedSettings] addObserver:self
forKeyPath:MUSIC_SHOULD_PLAY
options:NSKeyValueObservingOptionNew
context:nil];
}
- (void)dealloc {
[[Settings sharedSettings] removeObserver:self forKeyPath:MUSIC_SHOULD_PLAY];
}
// Here's where the KVO notifications are delivered.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqualToString:MUSIC_SHOULD_PLAY]) {
NSNumber *newValue = [change valueForKey:NSKeyValueChangeNewKey];
if ([newValue integerValue] == 1) {
NSLog(#"Request to turn music on");
} else {
NSLog(#"Request to turn music off");
}
}
}
#end
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.