I have an application with UITabBarController, where the first tab contains a ViewController of an HomePage. What I have to do, is to switch between the tabs (this is pretty simple: [[self tabBarController] setSelectedIndex:index]), AND to navigate through outlets of the selectedTab from the "HomePage".
Just to explain myself: TabElement1--->TabElementX---->UISegmentedController:segmentY
The problem is that the UISegmentedController is nil because it is not initialized yet (at least the first time I do the operation). How should I fix this problem? The tab elements are loaded with nibs.
EDIT-- Here's some code:
#implementation HomeViewController // Tab Indexed 0
// ...
- (void)playVideoPreview {
NSArray *array;
array = [[self tabBarController] viewControllers];
// This is a test where I programmatically select the tab AND the switch.
[[[array objectAtIndex:2] switches] setSelectedSegmentIndex:1];
[[self tabBarController] setViewControllers:array];
}
#end
#implementation TGWebViewController // Tab Indexed 2
// ...
#synthesize switches; // In .h file: #property (nonatomic, retain) IBOutlet UISegmentedControl switches; Properly linked within the XIB.
- (IBAction)switchHasChangedValue {
// Foo operations.
}
Now the first time I fire playVideoPreview I manage to get Into the Tab Indexed 2, TGWebViewController, but switches doesn't exists yet, so I find myself with the segmentedControl named "switches" with the first segment selected. If I get back to HomeViewController, then I fire again playVideoPreview, I get the correct behaviour.
I've fixed the problem using the delegates and a boolean. Now, when the loading of the ViewController at index 2 of TabBar is finished, it sends a message to its delegate which tells what segment has to be selected.
EDIT Here's the code (hope it helps):
// Method in the first View that asks to tab the tab bar to launch the other
// view controller
- (void)playVideoPreview {
NSArray *array;
array = [[self tabBarController] viewControllers];
if ( ![[array objectAtIndex:2] catSwitch] ) {
[[array objectAtIndex:2] setDelegate:self];
[[array objectAtIndex:2] setHasBeenLaunchedByDelegate:YES];
} else {
[self selectTab];
}
[[self tabBarController] setViewControllers:array];
[[self tabBarController] setSelectedIndex:2];
}
// Now the operations performed by the second View Controller
- (void)somewhereInYourCode {
if ( hasBeenLaunchedByDelegate ) {
[[self delegate] selectTab];
}
}
// In the First View Controller this is the delegate method,
// launched from the Second View Controller
- (void)selectTab {
NSArray *array;
array = [[self tabBarController] viewControllers];
[[[array objectAtIndex:2] catSwitch] setSelectedSegmentIndex:[[bannerPreview pageControl] currentPage]];
}
// Some declaration
#protocol SecondViewControllerDelegate;
class SecondViewController : ViewController {
id<TGWebViewControllerDelegate> delegate;
}
#end
#protocol SecondViewControllerDelegate
- (void)selectTab;
#end
// Meanwhile in the first view
class FirstViewController : ViewController <SecondViewControllerDelegate> {
// ...
}
Related
I'm creating a web App for iOS and I have a custom keyboard. When I use the default keyboard, it immediately shows the keyboard and the text field is above the keyboard. I would have the same result if I change the keyboard to my custom keyboard. But the problem is for the time that I gives the focus to the text field when my custom keyboard is enabled. It has some delay to load and when it loads, text field stays under the keyboard. Here is my code for custom keybaord:
#import "KeyboardViewController.h"
#import "CustomKeyboardView.h"
#interface KeyboardViewController () <CustomKeyboardViewDelegate>
#property (nonatomic, strong) CustomKeyboardView *customKeyboardView;
#end
#implementation KeyboardViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.customKeyboardView = [[CustomKeyboardView alloc] init];
self.inputView = (UIInputView *) self.customKeyboardView;
self.customKeyboardView.delegate = self;
}
- (void)handleCharacter:(NSString *)character sender:(CustomKeyboardView *)sender
{
[self.textDocumentProxy insertText:character];
}
-(void)handelDelete:(CustomKeyboardView *)sender
{
[self.textDocumentProxy deleteBackward];
}
-(void)handleDismissKeyboard:(CustomKeyboardView *)sender
{
[self dismissKeyboard];
}
-(void)handleChangeKeyboard:(CustomKeyboardView *)sender
{
[self advanceToNextInputMode];
}
- (void)textWillChange:(id<UITextInput>)textInput {
// The app is about to change the document's contents. Perform any preparation here.
}
- (void)textDidChange:(id<UITextInput>)textInput {
// The app has just changed the document's contents, the document context has been updated.
[[NSNotificationCenter defaultCenter] postNotificationName:UITextViewTextDidChangeNotification object:textInput];
}
#end
The implementation of CustomKeyboardView class is similar to this library. Any help would be appreciated.
Try use the background threads, you have a delay because u execute a lot of code before you build your keyboard view first.
I am trying to save a one-to-many relation in core data. There is a user decision involved to determine whether the new child list object needs to be attached to a new parent object. In the other case an existing database entry is used as a parent object. Under certain circumstances after saving, the app crashes.
FINAL EDIT: Sorry if you mind me keeping all of the edits, I still will. The process of enlightenment was quite convoluted. After all I started out thinking it was a data conflict... Thanks again to Tom, who helped point me in the right direction: I am still using a relation for sorting and grouping core data entities with an NSFetchedResultsController. I have written a valid compare: method for my entity class now and so far from what I can see it is working. I am about to write an answer for my question. I will still be very grateful for any information or warnings from you concerning this!
EDIT 3: The save procedure and the user alert seem to be incidental to the problem. I have zoomed in on the NSFetchedResultsController now and on the fact that I am using a relation ('examination') as sectionNameKeyPath. I will now try to write a compare: method in a category to my Examination entity class. If that does not work either, I will have to write a comparable value into my Image entity class in addition to the relation and use that for sections. Are y'all agreed?
EDIT 1: The crash only occurs after the user has been asked whether she wants a new examination and has answered YES. The same method is also entered when there was no user prompt (when the creation of a new examination has been decided by facts (no examination existing = YES, existing examination not timed-out = NO). In these cases the error does NOT occur. It must be that the view finishes loading while the alert view is open and then the collection view and its NSFetchedResultsController join the fun.
EDIT 2: Thanks to Tom, here is the call stack. I did not think it was relevant, but the view controller displays images in a collection view with sections of images per examination descending. So both the section key and the sort descriptor of the NSFetchedResultsController are using the examination after the MOCs change notification is sent. It is not the save that crashes my app: it is the NSSortDescriptor (or, to be fair, my way to use all of this).
The code for the NSFetchedResultsController:
#pragma mark - NSFetchedResultsController
- (NSFetchedResultsController *)fetchedResultsController
{
if (m_fetchedResultsController != nil) {
return m_fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
// Edit the entity name as appropriate.
NSEntityDescription *entity = [NSEntityDescription entityForName:NSStringFromClass([Image class]) inManagedObjectContext:[[HLSModelManager currentModelManager] managedObjectContext]];
[fetchRequest setEntity:entity];
// Set the batch size to a suitable number.
[fetchRequest setFetchBatchSize:20];
// Edit the sort key, identical sort to section key path must be first criterion
NSSortDescriptor *examinationSortDescriptor = [[NSSortDescriptor alloc] initWithKey:kexaminationSortDescriptor ascending:NO];
NSSortDescriptor *editDateSortDescriptor = [[NSSortDescriptor alloc] initWithKey:keditDateSortDescriptor ascending:NO];
NSArray *sortDescriptors =[[NSArray alloc] initWithObjects:examinationSortDescriptor, editDateSortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];
// Edit the section name key path and cache name if appropriate.
// nil for section name key path means "no sections".
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:[[HLSModelManager currentModelManager] managedObjectContext] sectionNameKeyPath:kSectionNameKeyPath cacheName:NSStringFromClass([Image class])];
aFetchedResultsController.delegate = self;
m_fetchedResultsController = aFetchedResultsController;
NSError *error = nil;
if (![self.fetchedResultsController performFetch:&error]) {
// Replace this implementation with code to handle the error appropriately.
// abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
HLSLoggerFatal(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return m_fetchedResultsController;
}
#pragma mark - NSFetchedResultsControllerDelegate - optional
/* Asks the delegate to return the corresponding section index entry for a given section name. Does not enable NSFetchedResultsController change tracking.
If this method isn't implemented by the delegate, the default implementation returns the capitalized first letter of the section name (seee NSFetchedResultsController sectionIndexTitleForSectionName:)
Only needed if a section index is used.
*/
- (NSString *)controller:(NSFetchedResultsController *)controller sectionIndexTitleForSectionName:(NSString *)sectionName
{
return sectionName;
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
// In the simplest, most efficient, case, reload the table view.
[[self collectionView] reloadData];
}
/* THE OTHER DELEGATE METHODS ARE ONLY FOR UITableView! */
The code for saving examination (existing or new) and the new image:
-(BOOL)saveNewImage
{
BOOL done = NO;
// remove observer for notification after alert
[[NSNotificationCenter defaultCenter] removeObserver:self name:kExaminationTimedoutAlertDone object:nil];
Examination * currentExamination = [self getCurrentExamination];
if ([self userWantsNewExamination] == YES)
{ // if an examination was found
if (currentExamination != nil)
{ // if the found examination is not closed yet
if ([currentExamination endDate] == nil)
{ // close examination & save!
[currentExamination closeExamination];
NSError *savingError = nil;
[HLSModelManager saveCurrentModelContext:(&savingError)];
if (savingError != nil)
{
HLSLoggerFatal(#"Failed to save old, closed examination: %#, %#", savingError, [savingError userInfo]);
return NO;
}
}
}
currentExamination = nil;
}
// the examination to be saved, either new or old
Examination * theExamination = nil;
// now, whether user wants new examination or no current examination was found - new examination will be created
if (currentExamination == nil)
{
// create new examination
theExamination = [Examination insert];
if (theExamination == nil)
{
HLSLoggerFatal(#"Failed to create new examination object.");
currentExamination = nil;
return NO;
}
// set new examinations data
[theExamination setStartDate: [NSDate date]];
}
else
{
theExamination = currentExamination;
}
if (theExamination == nil)
{ // no image without examination!
HLSLoggerFatal(#"No valid examination object.");
return NO;
}
Image *newImage = [Image insert];
if (newImage != nil)
{
// get users last name from app delegate
AppDelegate * myAppDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
// set image data
[newImage setEditUser: [[myAppDelegate user] lastName]];
[newImage setEditDate: [NSDate date]];
[newImage setExamination: theExamination];
[newImage setImage: [self stillImage]];
[newImage createImageThumbnail];
// update edit data
[theExamination setEditUser: [[myAppDelegate user] lastName]];
[theExamination setEditDate: [NSDate date]];
// unnecessary! CoreData does it automatically! [theExamination addImagesObject:newImage];
//! Important: save all changes in one go!
NSError *savingError = nil;
[HLSModelManager saveCurrentModelContext:(&savingError)];
if (savingError != nil)
{
HLSLoggerFatal(#"Failed to save new image + the examination: %#, %#", savingError, [savingError userInfo]);
}
else
{
// reload data into table view
[[self collectionView] reloadData];
return YES;
}
}
else
{
HLSLoggerFatal(#"Failed to create new image object.");
return NO;
}
return done;
}
The error:
2013-05-22 17:03:48.803 MyApp[11410:907] -[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0
2013-05-22 17:03:48.809 MyApp[11410:907] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0 with userInfo (null)
2013-05-22 17:03:48.828 MyApp[11410:907] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Examination compare:]: unrecognized selector sent to instance 0x1e5e73b0'
And here are the entity class files, too:
//
// Examination.h
// MyApp
//
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
#class Image;
#interface Examination : NSManagedObject
#property (nonatomic, retain) NSDate * editDate;
#property (nonatomic, retain) NSString * editUser;
#property (nonatomic, retain) NSDate * endDate;
#property (nonatomic, retain) NSDate * startDate;
#property (nonatomic, retain) NSSet *images;
#end
#interface Examination (CoreDataGeneratedAccessors)
- (void)addImagesObject:(Image *)value;
- (void)removeImagesObject:(Image *)value;
- (void)addImages:(NSSet *)values;
- (void)removeImages:(NSSet *)values;
#end
//
// Examination.m
// MyApp
//
#import "Examination.h"
#import "Image.h"
#implementation Examination
#dynamic editDate;
#dynamic editUser;
#dynamic endDate;
#dynamic startDate;
#dynamic images;
#end
This error had nothing to do with the saving of data to the MOC.
Because the saving of the new image data is triggered in the prepareForSegue of the previous view controller and the user alert gives the next view controller the time to finish loading, also creating the NSFetchedResultsController and its connection to its delegate, the exception was raised in the temporary context of the save to the MOC and only after the user alert.
The NSFetchedResultsController had started listening for changes of the MOC only in this case. It seems that if it gets alerted to an MOC change it will fetch only the changes and only then it needs to compare the new data with the existing data. Further information on this would be very welcome!
Then, because I had set a sort descriptor (and also the sectionNameKeyPath) to a relation and not provided the means to sort the entity objects in my core data entity class, the NSFetchedResultsController could not continue. Looking back it seems all so easy and natural, I really become suspicious of the simplicity of my solution...
I find it interesting that it could fetch the initial data in one go, when no change interfered. After all it was using the same NSSortDescriptor. Any ideas?
This is my solution:
//
// MyCategoryExamination.m
// MyApp
//
#import "MyCategoryExamination.h"
#implementation Examination (MyCategoryExamination)
- (NSComparisonResult)compare:(Examination *)anotherExamination;
{
return [[self startDate] compare:[anotherExamination startDate]];
}
#end
Please tell me if there is something wrong with this.
I've spent many hours trying to figure how to do this:
Having a placemark/annotation in the centerCoordinate of your mapView, when you scroll the map, the placemark should always stays in the center.
I've seen another app doing this too!
Found my question in How to add annotation on center of map view in iPhone?
There's the answer :
If you want to use an actual annotation instead of just a regular view positioned above the center of the map view, you can:
use an annotation class with a settable coordinate property (pre-defined MKPointAnnotation class eg). This avoids having to remove and add the annotation when the center changes.
create the annotation in viewDidLoad
keep a reference to it in a property, say centerAnnotation
update its coordinate (and title, etc) in the map view's regionDidChangeAnimated delegate method (make sure map view's delegate property is set)
Example:
#interface SomeViewController : UIViewController <MKMapViewDelegate> {
MKPointAnnotation *centerAnnotation;
}
#property (nonatomic, retain) MKPointAnnotation *centerAnnotation;
#end
#implementation SomeViewController
#synthesize centerAnnotation;
- (void)viewDidLoad {
[super viewDidLoad];
MKPointAnnotation *pa = [[MKPointAnnotation alloc] init];
pa.coordinate = mapView.centerCoordinate;
pa.title = #"Map Center";
pa.subtitle = [NSString stringWithFormat:#"%f, %f", pa.coordinate.latitude, pa.coordinate.longitude];
[mapView addAnnotation:pa];
self.centerAnnotation = pa;
[pa release];
}
- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
centerAnnotation.coordinate = mapView.centerCoordinate;
centerAnnotation.subtitle = [NSString stringWithFormat:#"%f, %f", centerAnnotation.coordinate.latitude, centerAnnotation.coordinate.longitude];
}
- (void)dealloc {
[centerAnnotation release];
[super dealloc];
}
#end
Now this will move the annotation but not smoothly. If you need the annotation to move more smoothly, you can add a UIPanGestureRecognizer and UIPinchGestureRecognizer to the map view and also update the annotation in the gesture handler:
// (Also add UIGestureRecognizerDelegate to the interface.)
// In viewDidLoad:
UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
panGesture.delegate = self;
[mapView addGestureRecognizer:panGesture];
[panGesture release];
UIPinchGestureRecognizer *pinchGesture = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleGesture:)];
pinchGesture.delegate = self;
[mapView addGestureRecognizer:pinchGesture];
[pinchGesture release];
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
centerAnnotation.coordinate = mapView.centerCoordinate;
centerAnnotation.subtitle = [NSString stringWithFormat:#"%f, %f", centerAnnotation.coordinate.latitude, centerAnnotation.coordinate.longitude];
}
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
//let the map view's and our gesture recognizers work at the same time...
return YES;
}
I have the following code but can't compile it because I have a "Type name requires a specifier or qualifier" error" for (self).
How to fix this error? I have compared it with the original code and there are no differences, so I don't know what's going on.
#import "CurrentTimeViewController.h"
#implementation CurrentTimeViewController
{
// Call the superclass's designated initializer
self = [super initWithNibName:#"CurrentTimeViewController"
bundle:nil];
if (self) {
// Get the tab bar item
UITabBarItem *tbi = [self tabBarItem];
// Give it a label
[tbi setTitle:#"Time"];
}
return self;
}
Here is the code from the mirror file HynososViewController.h, and which I cut, pasted and modified:
#import "HypnososViewController.h"
#implementation HypnososViewController
- (id) init
{
// Call the superclass's designated initializer
self = [super initWithNibName:nil
bundle:nil];
if (self) {
// Get the tab bar item
UITabBarItem *tbi = [self tabBarItem];
// Give it a label
[tbi setTitle:#"Hypnosis"];
}
return self;
}
- (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundle
{
// Disregard parameters - nib name is an implementation detail
return [self init];
}
// This method gets called automatically when the view is created
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Loaded the view for HypnosisViewController");
// Set the background color of the view so we can see it
[[self view] setBackgroundColor:[UIColor orangeColor]];
}
#end
Here is the complete code for CurrentTimeViewController.h:
#import "CurrentTimeViewController.h"
#implementation CurrentTimeViewController
{
// Call the superclass's designated initializer
self = [super initWithNibName:#"CurrentTimeViewController"
bundle:nil];
if (self) {
// Get the tab bar item
UITabBarItem *tbi = [self tabBarItem];
// Give it a label
[tbi setTitle:#"Time"];
}
return self;
}
- (id) initWithNibName:(NSString *)nibName bundle:(NSBundle *)Bundle
{
// Disregard parameters - nib name is an implementation detail
return [self init];
}
// This method gets called automatically when the view is created
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Loaded the view for CurrentTimeViewController");
// Set the background color of the view so we can see it
[[self view] setBackgroundColor:[UIColor greenColor]];
}
#end
Is the code above a cut-and-paste of EXACTLY what you are trying to compile? If so, I think you are missing something very important that would make that code block a method implementation:
-(id)init // This, or something like it, is missing???
{
...
}
Check your code, here, and in your project. :-)
MKMapView default, click on the pin view will appear with the title and subtitle of a small black box, but now I want to display the data more on a view. I want to display a customer view when I click on a pin view.
Has anyone done something similar? I have tried the following methods, but they did not work:
using a proxy access to the click event mapView: didSelectAnnotationView, but more than the delegate only supports ios4.x
use tapgesture to capture the event, only support after ios3.2
try to inherit MKMapView, found it impossible to get touch events.
also possible duplicate of MKMapView with custom MKAnnotation
Here is how you can create a custom callout
http://blog.asolutions.com/2010/09/building-custom-map-annotation-callouts-part-1/
Wondering how to solve
"3. try to inherit MKMapView, found it impossible to get touch events." on earlier than iOS4.x devices, here is how
Subclass MKAnnotationView, shown below ( .h and .m files )
.h file
#import <Foundation/Foundation.h>
#import <MapKit/MapKit.h>
#interface ClickableMapAnnotationView : MKAnnotationView {
SEL pinClicked;
id delegate;
}
#property(nonatomic, assign) SEL pinClicked;
#property(nonatomic, assign) id delegate;
#end
.m file
#import "ClickableMapAnnotationView.h"
#implementation ClickableMapAnnotationView
#synthesize pinClicked;
#synthesize delegate;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
if ( delegate != nil && [delegate respondsToSelector:pinClicked] )
[delegate performSelector:pinClicked withObject:self.annotation];
[super touchesBegan:touches withEvent:event];
}
#end
Then in UIViewController for mapView you need to set the ClickableAnnotation delegate and selector
-(MKAnnotationView *)mapView:(MKMapView )mapViewParam viewForAnnotation:(id )annotation {
static NSString PIN_RECYCLE_ID = #"pin";
if ( annotation == self.mapView.userLocation) // this is my location pin, skip
return nil;
ClickableMapAnnotationView* pin = (ClickableMapAnnotationView*)[self.mapView dequeueReusableAnnotationViewWithIdentifier: PIN_RECYCLE_ID];
if ( pin == nil ) {
pin = [[[ClickableMapAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier: PIN_RECYCLE_ID] autorelease];
// wire, pin clicked functionality
pin.delegate = self;
pin.pinClicked = #selector(annotationViewClicked:);
....
-(void) annotationViewClicked:(id) sender {
// map pin has been clicked
}