accessibilityLabel and accessibilityHint properties aren't always loaded from NIB - iphone

The UI of my iPhone application has lots of static labels, and I have set accessibility hints for them in Interface Builder. I want to access these programmatically so I can provide help bubbles - a custom subclass of UILabel recognises a touch and displays a bubble with the value of [self accessibilityHint].
However, [self accessibilityHint] returns nil. If I set the value programmatically [self setAccessibilityHint: #"Hello"] then I can access that value from my program, but the initial value from the NIB isn't available.
If I turn on the Accessibility Inspector before launching my application, the hints from the NIB files are available via the accessibilityHint property. Is there some flag somewhere that determines whether the system loads these properties; and if there is, is there some way I can set it?
My backup option is to have my controllers keep references to each UI label and set the accessibilityHint in code, but that's pretty ugly and cumbersome.

Hmm, if I open Library/Preferences/com.apple.Accessibility.plist and change ApplicationAccessibilityEnabled from false to true, then it works. (That path being in ~/Library/Application Support/iPhone Simulator/)
I tried adding this at the start of main():
CFPreferencesSetAppValue(#"AccessibilityEnabled", kCFBooleanTrue, #"com.apple.Accessibility");
CFPreferencesSetAppValue(#"ApplicationAccessibilityEnabled", kCFBooleanTrue, #"com.apple.Accessibility");
CFPreferencesAppSynchronize(#"com.apple.Accessibility");
but it didn't work. (It wrote a file to Applications/{UUID}/Library/Preferences/com.apple.Accessibility.plist)
EDIT: After stepping through the UIKit code, the call that determines whether accessibility is turned on or off is
CFPreferencesGetBooleanValue(#"ApplicationAccessibilityEnabled", #"/Users/sam/Library/Application Support/iPhone Simulator/User/Library/Preferences/com.apple.Accessibility", NULL);
Note the bizarre app key, I'm not yet sure where this value comes from (my knowledge of 386 assembly is very weak!) but I can pass this key to CFPreferencesSetAppValue and it works, at least on the simulator (I don't have access to an actual device at the moment).
Also this will turn on app accessibility for all applications (since it writes it to the global plist). I can set a flag from main() if the value should be set back to false once the application has launched.

This works on simulator and device. Taken from http://sgleadow.github.com/blog/2011/11/16/enabling-accessibility-programatically-on-ios-devices/
#import <dlfcn.h>
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
NSString *appSupportLocation = #"/System/Library/PrivateFrameworks/AppSupport.framework/AppSupport";
NSDictionary *environment = [[NSProcessInfo processInfo] environment];
NSString *simulatorRoot = [environment objectForKey:#"IPHONE_SIMULATOR_ROOT"];
if (simulatorRoot) {
appSupportLocation = [simulatorRoot stringByAppendingString:appSupportLocation];
}
void *appSupportLibrary = dlopen([appSupportLocation fileSystemRepresentation], RTLD_LAZY);
CFStringRef (*copySharedResourcesPreferencesDomainForDomain)(CFStringRef domain) = dlsym(appSupportLibrary, "CPCopySharedResourcesPreferencesDomainForDomain");
if (copySharedResourcesPreferencesDomainForDomain) {
CFStringRef accessibilityDomain = copySharedResourcesPreferencesDomainForDomain(CFSTR("com.apple.Accessibility"));
if (accessibilityDomain) {
CFPreferencesSetValue(CFSTR("ApplicationAccessibilityEnabled"), kCFBooleanTrue, accessibilityDomain, kCFPreferencesAnyUser, kCFPreferencesAnyHost);
CFRelease(accessibilityDomain);
}
}
[autoreleasePool drain];

Related

using string variable from one class in another

Rookie question: I am writing a program that will generate a specific string and then display it in a text window in a different view controller. I have been testing to ensure that the code in fact generates the string using NSLog commands and I know the code is working as intended. For some reason it is not transferring across the view controller and I cant figure out why. Any help? Here is a snippet of the code:
CreateStoryViewController.m
- (IBAction)makeStory:(id)sender
{
StoryLine *myStory =[[StoryLine alloc] init];
[myStory setStory];
self.story = myStory.plot;
NSLog(#"story is %#", self.story);//this is generating the correct story string
self.displayStoryController = [[BIDDisplayStoryViewController alloc] initWithNibName:#"DisplayStoryView" bundle:nil];
[self.view insertSubview:self.displayStoryController.view atIndex:1];
}
DisplayStoryViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
BIDCreateStoryViewController *newStory = [[BIDCreateStoryViewController alloc] init];
NSLog(#"newStory.story is %#",newStory.story);//this generates null message
self.storyDisplay.text = newStory.story;
}
This is wrong. You're instantiating a new BIDCreateViewController object inside your second view controller. This is not the same as the original BIDCreateViewController object that pushed your second BIDDisplayStoryViewController.
You need to declare a string property in your BIDDisplayStoryViewController's header file.
Something like
#property (nonatomic, retain /*or strong, if using ARC*/) NSString *storyToDisplay;
Be sure to synthesize this in your implementation file as well.
When you create BIDDisplayStoryViewController inside your first view controller, you need to do it as follows:
self.displayStoryController = [[BIDDisplayStoryViewController alloc] initWithNibName:#"DisplayStoryView" bundle:nil];
self.displayStoryViewController.storyToDisplay = self.story;
Now inside your second view controller you can access this using self.myStory.
While this will solve your problem (and please do understand that it's not my intention to be rude here), I feel that there's a lack of understanding of how iOS (and OOP in general) works.
In your viewDidLoad method you are making a whole new story. This story is totally different from the one you made in the makeStory: method. You should add a StoryLine Property to DisplayStoryViewController.h, and set that after you init your displayStoryController.
make the intended variable a property type at .h file, so the other file can access it

How to update the whole UI according to the configuration the user selects

I have a configfile1.plist file in my app bundle that includes all the required settings for the UI of the app, i.e. the background images, the animations, the sounds, database, and anything else. I have used a ConfigFileManager class in which there's this method as follows:
-(void) loadConfigFile
{
configFile = [[NSBundle mainBundle] pathForResource:#"ConfigFile1"
ofType:#"plist"];
rootDictionary = [[NSDictionary alloc]initWithContentsOfFile:configFile];
//__ settings
settings = [rootDictionary objectForKey:#"settings"];
timerMode = [settings objectForKey:#"timerMode"];
timePerQuestion = [settings objectForKey:#"timePerQuestion"];
if (timerMode.intValue == 1) {
//__time is fixed.
}
else
{
//time is not fixed, there's an initialTime to begin with, and it increases if the answer is correct (timePerQuestion), and doesn't change if it's wrong.
initialTime = [settings objectForKey:#"initialTime"];
}
//__ Textures
textures = [rootDictionary objectForKey:#"textures"];
//__ buttons
buttons = [textures objectForKey:#"buttons"];
buttonBackground = [buttons objectForKey:#"buttonBG"];
buttonBackgroundSelected = [buttons objectForKey:#"buttonBGSelected"];
mainMenuPlayButton = [buttons objectForKey:#"mainMenu.playButton"];
.
.
.
}
This is the default configuration for the app for which the method is called in the app delegate while the app is being opened. Now let's say I have configfile2.plist, configfile3.plist, and so on. I have a UIViewController for this, too, that must display the available config files in a table view. What should I do this, so that the user can select one of the config files, and that gets applied to the whole app?
Any detailed response will be appreciated.
Thanks in advance!
The general idea is like this:
a .plist stores possible options
shows the options to user
save the selected options to somewhere
refresh UI based on saved options
You have done step 1 and 2. So you have to decide where to store the options for later retrieval.
One option is to use [NSUserDefaults standardUserDefaults] to save selected options. You may check out InAppSettingsKit and its sample app to see how the whole thing work.

Error getting artwork for current song

Grabbing album art for current song and using it to change a certain imageView.image generates an error, but no longer crashes. (It did before because I left out the if (!artwork) error handling. Eheh.)
This method:
- (void)handleNowPlayingItemChanged:(id)notification {
MPMediaItem *item = self.musicPlayer.nowPlayingItem;
CGSize albumCoverSize = self.albumCover.bounds.size;
MPMediaItemArtwork *artwork =
[item valueForProperty:MPMediaItemPropertyArtwork];
if (artwork) {
self.albumCover.image = [artwork imageWithSize:albumCoverSize];
} else {
self.albumCover.image = nil;
}
}
Explodes like this:
CPSqliteStatementPerform: attempt to write a readonly database for
UPDATE ddd.ext_container SET orig_date_modified = (SELECT date_modified
FROM container WHERE pid=container_pid) WHERE orig_date_modified=0
CPSqliteStatementReset: attempt to write a readonly database for
UPDATE ddd.ext_container SET orig_date_modified = (SELECT date_modified
FROM container WHERE pid=container_pid) WHERE orig_date_modified=0
But only on launch. And it still shows the image (or lack thereof). Weird.
Edit: The iPod Library is readonly (apps can't change anything, only iTunes), so maybe it's yelling at
me for writing a readonly something, but still allowing it because nothing readonly is being modified?
And after that's fixed, I need to get resizing working (for Landscape support) instead of IB's stretching.
Not vital, but still a nice thing to have.
Here's what I do. It creates no errors, and produces an image every time. If the song doesn't have an image, it defaults to the one I provide. I think because you're not checking for an image with a specific size (320 by 320, matching the screen width for me), it fails to figure it out correctly. I don't know why you're getting the SQLite error, but hopefully this fixes it!
MPMediaItemArtwork *artworkItem = [self.musicPlayer.nowPlayingItem valueForProperty: MPMediaItemPropertyArtwork];
if ([artworkItem imageWithSize:CGSizeMake(320, 320)]) {
[self.currentlyPlayingArtworkView setImage:[artworkItem imageWithSize:CGSizeMake (320, 320)]];
}
else {
[self.currentlyPlayingArtworkView setImage:[UIImage imageNamed:#"NoArtworkImage"]];
}
Link here - Why am I getting this CPSqliteStatementPerform error in xcode console
Putting this here so the question can be marked as Answered.

Using built-in icons for mime type or UTI type in iOS

Problem:
I would like to be able to use the built-in iOS icons for standard mime types (or UTI types) in my listing of binary file content.
Background:
I have looked into using the new (since 3.2) document architecture, but using the UIDocumentInteractionController it seems that the assumption is that the actual binaries are already on the local device.
In my case I have a file listing from a remote server and know the mime type, name, title, etc for the remote file so I just want to show a file listing with icons (the actual binary is only loaded as needed).
The meta data I get from the server contains proper mime types for the binaries so in theory I just want to get the system icon based on the type.
Work around?
I have tried the following "hack" as a proof of concept and it seems to work but this doesn't seem like the best way to go...
//Need to initialize this way or the doc controller doesn't work
NSURL*fooUrl = [NSURL URLWithString:#"file://foot.dat"];
UIDocumentInteractionController* docController = [[UIDocumentInteractionController interactionControllerWithURL:fooUrl] retain];
UIImage* thumbnail = nil;
//Need to convert from mime type to a UTI to be able to get icons for the document
NSString *uti = [NSMakeCollectable(UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, (CFStringRef)self.contentType, NULL)) autorelease];
//Tell the doc controller what UTI type we want
docController.UTI = uti;
//The doc controller now seems to have icon(s) for the type I ask for...
NSArray* icons = docController.icons;
if([icons count] > 0) {
thumbnail = [icons objectAtIndex:0];
}
return thumbnail;
You can create a UIDocumentInteractionController without needing to specify a URL. The header for the class says the icons are determined by name if set, URL otherwise.
UIDocumentInteractionController* docController = [[UIDocumentInteractionController alloc] init];
docController.name = #"foo.dat";
NSArray* icons = docController.icons;
// Do something with icons
...
[docController release];
I tried Ben Lings's solution, but it didn't work on iOS6.1 in either the simulator or on my iPad3. You need to provide an NSURL to the UIDocumentInteractionController, but that URL doesn't need to exist. Its last path component just needs to have the extension that you want.
The following code worked for me
NSString *extension = #"pptx"; // or something else
NSString *dummyPath = [#"~/foo" stringByAppendingPathExtension:extension]; // doesn't exist
NSURL *URL = [NSURL fileURLWithPath:dummyPath];
UIDocumentInteractionController *documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:URL];
NSArray *systemIconImages = documentInteractionController.icons;
return systemIconImages;
So we are talking about hacks uh? I did this by doing some bad stuff, but it's working...
I copied the icons from /system/library/frameworks/QuickLook.framework and added to my project. Inside this same folder, there is some property lists, that make the link between the UTI/extension/mime-type with the png file. With the plist and the pngs, all you have to do is make a logic to read the plists and show the correct png.

I'm having issues inputing a name if applicable

I have a function here that upon completing a single round, if your score is higher than either a default score entry or a newly placed high score then it will swap its data with your data and push everything else down. removing the last entry from the list. currently this is just one exchange and for functions sake I'm going to hard code it and then refactor it later.
My main problem is that when I set up a text input view to capture the players name execution continues immediately without the players input and crashes the game. I commented out the line that sets the text because I have a default value in place just in case any attempt that I try to make fails. How can I get Execution to wait for a moment while input is taken? Would I have to set up a delegate method? If so I'm still a bit confused by delegates. I could set it up to work but I don't understand it, so I wouldn't be able to do any other special custom tasks with it. I've worked on it for a while and got no further...
-(void)saveData:(ScoreKeep *)stats{
NSMutableDictionary *swap = [[NSMutableDictionary alloc]init];//used for swaping entries
NSString *filePath = [self pathOfFile];
NSLog(#"Writing to %#", filePath);
if ([[NSFileManager defaultManager]fileExistsAtPath:filePath]) {
NSLog(#"Loading previous dictionary to save...");
dataDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:filePath];
if ([dataDictionary objectForKey:#"1"]) {
NSMutableDictionary *highScore = [dataDictionary objectForKey:#"1"];
if ([stats.score intValue] > [[highScore objectForKey:#"SCORE"] intValue]) {
NSLog(#"You Win! score: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
NSLog(#"Setting up name entry");
[self.view addSubview:stats.view]; //New view is added so that the player can input data(Assume it is complete);
//stats.nameTag = setName.nameTag;//This line is executed before the new view is dismissed causing an error to occur
[stats setupDictionary]; // It just goes down hill from here if the previous line is uncommented
[dataDictionary setObject:stats.sComponents forKey:#"1"];
}else {
NSLog(#"You Lose: %# highscore: %#", stats.score,[NSNumber numberWithInt:[[highScore objectForKey:#"SCORE"] intValue]] );
}
NSLog(#"Got first place entry");
}else {
NSLog(#"Initilizing Score");
}
}else{
NSLog(#"Creating new dictionary to save...");
dataDictionary = [[NSMutableDictionary alloc]init];
}
[dataDictionary writeToFile:filePath atomically:YES];
}
Help would greatly be appreciated. If more information is needed I'd be happy to provide.
by the way ScoreKeep is an object that contains a dictionary and a function to create a dictionary such that it can set any values I need and package them into sComponents(the dictionary to be entered into the main savefile)
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#class omphalosUtility;
#pragma mark -
#pragma mark Saving data
#pragma mark -
static inline void poop(){
NSLog(#"POOP");
}
I'm going to try making a utility file that works independently of the app so that I Can update files and perform other universal operations such as saving when needed. Its a step in a direction that i'd like to take.
If i get it right, (The code is really nasty, man...) your problem is that you are trying to present a View Controller with the wrong way.
Correct me if i'm wrong, is ScoreKeep is a ViewController? if so, you have to name it properly. that's for a start.
Second, you cant present another view controller only by adding its "view" property to the current view controller's View Hierarchy. that way the view will not respond properly to the events.
the correct way to to what you'r trying to do is by presenting the ScoreKeep ViewController modally.
there is no other right way to do this without using delegation. you will have to acquire this technique.
Your view controller that responsible for getting the name from the user need to have a way to tell it's master view controller that the user entered a name. and that is achieved through delegation.
What you should do:
Basically you create a protocol called something like "NamePrompterViewControllerDelegate"
that will have at least one method that will be called when the user will done entering his name.
Your ScoreKeepViewController should have an instance variable that implemented the protocol (Look at the apple documentation on protocols for assistance)
Your main view controller (the one that contains the method you added) then should implement the protocol you created, and set itself as the delegate of ScoreKeep like that:
stats = [[ScoreKeep alloc] initWithNibName:#"Scorekeep" bundle:nil];
stats.delegate = self;
For more info on presenting and dismissing ViewControllers modally you should read the documentation at Apple Documentation
I hope i helped you, there is just a lot to cover and it hardly can be done by writing an answer.
Feel free to ask more for clearance.