Is this code [self configureView] in the setSighting: necessary which reappears later? - objective-c

#import "BirdsDetailViewController.h"
#import "BirdSighting.h"
#interface BirdsDetailViewController ()
- (void)configureView;
#end
#implementation BirdsDetailViewController
-(void)setSighting:(BirdSighting *)sighting
{
if (_sighting != sighting) {
_sighting = sighting;
//Update the view
[self configureView];
// Is this code[self configureView] necessary which reappears in the later viewDidLoad?
//After I deleted this line everything seems works well.
}
}
- (void)configureView
{
// Update the user interface for the detail item.
BirdSighting *theSighting = self.sighting;
static NSDateFormatter *formatter = nil;
if (formatter == nil) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateStyle:NSDateFormatterMediumStyle];
}
if (theSighting) {
self.birdNameLable.text = theSighting.name;
self.locationLable.text = theSighting.location;
self.dateLable.text = [formatter stringFromDate:(NSDate *) theSighting.date];
}
}
- (void)viewDidLoad
{
[super viewDidLoad];
//You see. It reappears here.
[self configureView];
}
#end
The code above is quoted from Apple's official sample(Your Second iOS App: Storyboards ).
Is this code [self configureView] in the setSighting: necessary which reappears later ?After I deleted this line everything seems works well.
Thank you very much.

You can ommit [self configureView]; in setSighting:.
And One more thing why you are implementing setSighting: yourself, Cocoa provides you #property/#synthesize, that is trusted and will be shorter for your code as you are doing nothing specific.

Related

nsmutabledictionary returning null when accessed outside of class

I have searched around a bit and not found an answer to my question. I am a beginner to objective-c and I am currently experimenting around with dictionaries. I have a class called "SETestBank" that creates 3 dictionaries of images and then adds them to a large dictionary. I am doing it this way because down the line I may add more smaller dictionaries. I've created a specific "accessBank" method to pull objects out via other classes.
SETestBank.m
#import "SETestBank.h"
#implementation SETestBank
#synthesize mathBankA, mathBankB, mathBankC, mathTest;
- (id)init
{
[self createBankA];
[self createBankB];
[self createBankC];
[self createTest];
return 0;
}
- (NSMutableDictionary *)createBankA
{
mathBankA = [[NSMutableDictionary alloc] init];
for (int i=0; i < 11; i++) {
NSString *aKey = [NSString stringWithFormat:#"%da", i];
NSString* imageName = [NSString stringWithFormat:#"%da.png",i];
[mathBankA setObject:imageName forKey:aKey];
NSLog(#"%#", aKey);
}
return mathBankA;
}
//same occurs to generate bankB and bankC
- (NSMutableDictionary *)createTest
{
mathTest = [[NSMutableDictionary alloc] init];
[mathTest addEntriesFromDictionary:mathBankA];
[mathTest addEntriesFromDictionary:mathBankB];
[mathTest addEntriesFromDictionary:mathBankC];
return mathTest;
}
- (NSString *)accessBank:(NSString *)accessor
{
NSString *img = [mathTest objectForKey:accessor];
return img;
}
#end
now this code seems to work fine. The dictionaries are created and console logs display the correct keys and object filenames (the images are all in the project and properly named)
However, when I access it in my view controller and try to apply an image to a UIImageView I get nothing.
SEViewController:
- (void)viewDidLoad
{
SETestBank *mathTest = [[SETestBank alloc] init];
UIImage *img = [UIImage imageNamed:[mathTest accessBank:#"1a"]];
NSString *test = [mathTest accessBank:#"1a"];
NSLog(#"%#", test);
[imageView setImage:img];
[self.view addSubview:imageView];
[super viewDidLoad];
}
the log here simply returns null along with a "CUICatalog: Invalid asset name supplied: (null), or invalid scale factor: 1.000000" error which I gather is from trying to assign null to an image. I'm stumped on this one. If I hardcode the image to display "1a.png" rather than trying to access it by key it works fine but that is not what I'm trying to accomplish. imageView is connected to a UIImageView in storyboard and the view controller is set to use SEViewController as it's class.
Thanks for any help!
In the init method, you return a 0, which means nothing (nil)
- (id)init
{
[self createBankA];
[self createBankB];
[self createBankC];
[self createTest];
return 0;
}
Change this to
- (id)init
{
if (self = [super init]) {
[self createBankA];
[self createBankB];
[self createBankC];
[self createTest];
}
return self;
}
may solve your problem.
Your init in SETestBank is returning 0, so your mathTest object is nil when you do
SETestBank *mathTest = [[SETestBank alloc] init];

why is this OCUnit test failing?

It's stepping into the ViewDidLoad of the main view controller, and hitting the line calling get all tweets, but I put a breakpoint in the getAllTweets of both the base and derived to see if it just wasn't hitting the derived like I expected.
#implementation WWMainViewControllerTests {
// system under test
WWMainViewController *viewController;
// dependencies
UITableView *tableViewForTests;
WWTweetServiceMock *tweetServiceMock;
}
- (void)setUp {
tweetServiceMock = [[WWTweetServiceMock alloc] init];
viewController = [[WWMainViewController alloc] init];
viewController.tweetService = tweetServiceMock;
tableViewForTests = [[UITableView alloc] init];
viewController.mainTableView = tableViewForTests;
tableViewForTests.dataSource = viewController;
tableViewForTests.delegate = viewController;
}
- (void)test_ViewLoadedShouldCallServiceLayer_GetAllTweets {
[viewController loadView];
STAssertTrue(tweetServiceMock.getAllTweetsCalled, #"Should call getAllTweets on tweetService dependency");
}
- (void)tearDown {
tableViewForTests = nil;
viewController = nil;
tweetServiceMock = nil;
}
The base tweet service:
#implementation WWTweetService {
NSMutableArray *tweetsToReturn;
}
- (id)init {
if (self = [super init]) {
tweetsToReturn = [[NSMutableArray alloc] init];
}
return self;
}
- (NSArray *)getAllTweets {
NSLog(#"here in the base of get all tweets");
return tweetsToReturn;
}
#end
The Mock tweet service:
#interface WWTweetServiceMock : WWTweetService
#property BOOL getAllTweetsCalled;
#end
#implementation WWTweetServiceMock
#synthesize getAllTweetsCalled;
- (id)init {
if (self = [super init]) {
getAllTweetsCalled = NO;
}
return self;
}
- (NSArray *)getAllTweets {
NSLog(#"here in the mock class.");
getAllTweetsCalled = YES;
return [NSArray array];
}
The main view controller under test:
#implementation WWMainViewController
#synthesize mainTableView = _mainTableView;
#synthesize tweetService;
NSArray *allTweets;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
allTweets = [tweetService getAllTweets];
NSLog(#"was here in view controller");
}
- (void)viewDidUnload
{
[self setMainTableView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
}
Since you're able to break in the debugger in viewDidLoad, what's the value of the tweetService ivar? If it's nil, the getAllTweets message will just be a no op. Maybe the ivar isn't being set properly or overridden somewhere else.
You should probably use the property to access the tweetService (call self.tweetService) rather than its underlying ivar. You should only ever access the ivar directly in getters, setters, and init (also dealloc if aren't using ARC for some crazy reason).
You also should not call loadView yourself, rather just access the view property of the view controller. That will kick off the loading process and call viewDidLoad.
Also, if you're doing a lot of mocking, I highly recommend OCMock.

Singleton not updating variable immediately

I have a singleton here is the header file:
#import <Foundation/Foundation.h>
#interface Shared : NSObject
{
NSString *messages;
}
#property (nonatomic, retain) NSString *messages;
+ (Shared*)sharedInstance;
#end
Here is the implementation:
#import "Shared.h"
static Shared* sharedInstance;
#implementation Shared
#synthesize messages;
+ (Shared*)sharedInstance
{
if ( !sharedInstance)
{
sharedInstance = [[Shared alloc] init];
}
return sharedInstance;
}
- (id)init
{
self = [super init];
if ( self )
{
messages = [[NSString alloc] init];
}
return self;
}
#end
The problem is when the I use
[Shared sharedInstance].messages = someVariable;
I can use
NSLog([Shared sharedInstance].messages);
and it shows the right output, but when i check from another class, NSLog doesn't show any output. I have the NSLog in the viewDidLoad method of another class, so when I click a button to go to the next view, it should output the value of the string, but it only works the second time. If the variable is set to dog, first it outputs nothing, then when I close the view and try again, it outputs dog. however, if I then change the variable to cat, it will output dog, and on the next attempt, output cat. I want it to update immediately, rather than remain one behind all the time.
EDIT: Here's the code from the other classes
This particular section is from a view controller class in the method
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
//Omitted, just preparing the DB, and emptying the array.
if ([db open])
{
FMResultSet *s = [db executeQueryWithFormat:#"SELECT ShabadID FROM Shabad WHERE Gurmukhi LIKE %#", currentLine];
while ([s next])
{
lineID = [s intForColumn:#"ShabadID"];
}
s = [db executeQueryWithFormat:#"SELECT Gurmukhi, ShabadID FROM Shabad WHERE ShabadID LIKE %i", lineID];
while ([s next])
{
//NSLog([s stringForColumn:#"Gurmukhi"]);
[paragraphArray addObject:[s stringForColumn:#"Gurmukhi"]];
}
Text = #"";
for (int i = 0; i<[paragraphArray count]; i++)
{
Text = [Text stringByAppendingFormat:#"%#\n", [paragraphArray objectAtIndex:i]];
}
[Shared sharedInstance].messages = Text;
}
Then in the another class, where I want the text to appear, in the viewDidLoad method,
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog([Shared sharedInstance].messages);
UITextView *myUITextView = [[UITextView alloc] initWithFrame:CGRectMake(0,30,310,450)];
myUITextView.text = [Shared sharedInstance].messages;
myUITextView.textAlignment = NSTextAlignmentCenter;
myUITextView.textColor = [UIColor blackColor];
myUITextView.font = [UIFont fontWithName:#"GurbaniLipiLight" size:24];
[myUITextView setBackgroundColor:[UIColor clearColor]];
myUITextView.editable = NO;
myUITextView.scrollEnabled = YES;
[ScrollerView addSubview:myUITextView];
}
Sure the NSLog doesn't show up right, but neither does the text in the textview, it does the same thing the NSLog does.
There is an assumption here about what order things happen in that's not quite right. Assuming there's a segue involved in this, didSelectRowAtIndexPath: is called after the new view controller is prepared but before it's displayed. Moving code to viewWillAppear: or viewDidAppear: delays execution until after the calling controller has set new data.
The other approach for communication between controllers that use a segue, is to use prepareForSegue: in the first controller to set data that the second controller needs. That way it should be available when the view is loaded.

Populating an IKImageBrowserView

I am currently working on building up a view which shows icons, and text labels to the right of the icons. After some searching, I've decided that an IKImageBrowserView is most suitable. As such, I've gone ahead and created my IKImageBrowserView and set it's data source as follows:
// Setup image browser view
IKImageBrowserView *browser = [IKImageBrowserView new];
// Build our datasource delegate
MyDataStore *ds = [[MyDataStore alloc] init];
[browser setDelegate:ds];
[browser setDataSource:ds];
NSImage *image = // . . . File path
NSString *imageID = // . . . Filename
IKBBrowserItem *item = [[IKBBrowserItem alloc] initWithImage:image imageID:imageID];
[[ds images] addObject:item];
I've also created my data source, implementing the two required methods in the protocol:
import "MyDataStore.h"
#implementation MyDataStore
- (id) init {
if ([super init] != nil) {
images = [NSMutableArray new];
}
return self;
}
- (NSUInteger) numberOfItemsInImageBrowser:(IKImageBrowserView *) aBrowser {
return [images count];
}
- (id) imageBrowser:(IKImageBrowserView *) aBrowser itemAtIndex:(NSUInteger)index {
return [images objectAtIndex:index];
}
- (NSMutableArray *) images {
return images;
}
#end
However, when I try and add an item (as shown in the first block of code), nothing shows up in my IKImageBrowserView. I suspect I'm doing something glaringly incorrect with this view. Anyone know what that may be?

NSWindowController not hiding its window at init?

I have an NSDocument subclass with two NSWindowControllers corresponding to 2 different xib.
Following the Document-Based Application Guide I have added the following in my document.m implementation
- (void)makeWindowControllers
{
NSLog(#"in MakeWindowControllers");
MainWindowController *mainWindowController = [[MainWindowController alloc] init];
[mainWindowController autorelease];
[self addWindowController:mainWindowController];
csvWindowController = [[CSVWindowController alloc] init];
[csvWindowController autorelease];
[self addWindowController:csvWindowController];
}
Problem is I want the second window controller csvWindowController to hide its window initially, I will show the same instance of the window later on. To do so I have written:
#implementation CSVWindowController
- (id) init {
if ( ! (self = [super initWithWindowNibName:#"CSVWindow"]) ) {
NSLog(#"CSVWindowController init failed");
return nil;
}
window = [self window];
NSLog(#"CSVWindowController init");
[window orderOut:nil]; // to hide it
NSLog(#"CSVWindowController hiding the window");
return self;
}
But the window is there, showing up.
Please not I have the VisibleAtLaunch not flagged, that console it's showing my messages correctly, and that even if I change:
[window orderOut:nil]; // to hide it
to
[window orderOut:self]; // to hide it
The result is the same, window showing up.
Any help is appreciated, thanks :)
Ok, again I reply to my own question, but this time with a positive remark. I think what I was doing wrong had something to do with the hidden - for me - implications of the Document-based architecture of the default Document Application template.
I have tried with a different approach, creating an application from scratch NOT flagging "Document-based Application" and providing it with:
1 NSDocument subclass
2 NSWindowControllers subclasses
1 MainMenu.xib
2 window.xib
and I have forced instantiation of the NSWindowController subclasses in the MyDocument code.
I have also put the IBActions for the MenuItems in the MyDocument and I have bound the MyDocument Object to the MenuItems in the MainMenu.xib.
This time I was able to do whatever, hiding/showing windows starting with one hidden one not, enabling menu items automatically at will.
Here follows the code, for any newbie like me who might have to fight with this in the future.
// MyDocument.h
#import <Cocoa/Cocoa.h>
#import "testWindowController.h"
#import "test2WindowController.h"
#interface MyDocument : NSDocument {
testWindowController *test;
test2WindowController *test2;
}
- (IBAction)showWindow1:(id)pId;
- (IBAction)showWindow2:(id)pId;
- (IBAction)hideWindow1:(id)pId;
- (IBAction)hideWindow2:(id)pId;
#end
// MyDocument.m
#import "MyDocument.h"
#import "testWindowController.h"
#import "test2WindowController.h"
#implementation MyDocument
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
NSLog(#"MyDocument init...");
[self makeWindowControllers];
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
- (void)makeWindowControllers
{
test = [[testWindowController alloc] init];
test2 = [[test2WindowController alloc] init];
[self addWindowController:test];
[self addWindowController:test2];
// start hiding the first window
[[test window] orderOut:self];
}
- (IBAction)hideWindow1:(id)pId
{
NSLog(#"hideWindow1");
[[test window] orderOut:self];
}
- (IBAction)showWindow1:(id)pId
{
NSLog(#"showWindow1");
[test showWindow:self];
[[test window] makeKeyAndOrderFront:nil]; // to show it
}
- (IBAction)hideWindow2:(id)pId
{
NSLog(#"hideWindow2");
[[test2 window] orderOut:self];
}
- (IBAction)showWindow2:(id)pId
{
NSLog(#"showWindow2");
[test2 showWindow:self];
[[test2 window] makeKeyAndOrderFront:nil]; // to show it
}
-(BOOL)validateMenuItem:(NSMenuItem *)menuItem {
NSLog(#"in validateMenuItem for item: %#", [menuItem title]);
if ([[menuItem title] isEqualToString:#"Show Window"]
&& [[test window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Hide Window"]
&& ![[test window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Show Window2"]
&& [[test2 window] isVisible]){
return NO;
}
if ([[menuItem title] isEqualToString:#"Hide Window2"]
&& ![[test2 window] isVisible]){
return NO;
}
return [super validateMenuItem:menuItem];
}
This is another method to prevent NSDocument's window(s) to show up early:
Subclass NSDocuments's window in IB
Use a flag to signal when window content is ready
Override makeKeyAndOrderFront method.
#interface DocWindow : NSWindow
#property BOOL inited;
#end
#implementation DocWindow
- (void)makeKeyAndOrderFront:(id)sender
{
if ( _inited )
[super makeKeyAndOrderFront:sender];
}
#end
#implementation Document
- (void)windowControllerDidLoadNib:(NSWindowController *)windowController
{
// prepare window content here.
...
// show doc's window when ready
DocWindow *win = (DocWindow *)self.window;
win.inited = YES;
[win makeKeyAndOrderFront:self];
}
#end