Can an editable UITextView be embedded in a UIPopoverController? - ios

I'm attempting to embed an editable UITextView inside a UIPopoverController, with... strange results. The steps I've taken are:
Create a custom UIViewController class, and create a .xib file with that controller with a UITextView inside it.
When the UI action that should bring up the controller occurs (touch up inside), I instantiate the controller and its view from the .xib file.
I create a new UIPopoverController, with the view controller I just instantiated as the content view.
I present it with presentPopoverFromRect:inView:permittedArrowDirections:animated:
Here's some example code:
- (void)noteButtonPressed:(id)sender {
self.noteview = [[MyTextPopupViewController alloc] initWithNibName:#"MyTextPopupViewController" bundle:nil ];
UIPopoverController *popoverController = [[UIPopoverController alloc] initWithContentViewController:self.noteview];
self.popover = popoverController;
self.popover.delegate = self;
[self.popover presentPopoverFromRect:((UIView*)sender).frame
inView:self.view
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
}
Then, inside MyTextPopupViewController, I make sure the the text view is the first responder to bring up the keyboard:
- (void)viewDidAppear:(BOOL)animated {
[self.view becomeFirstResponder];
[super viewDidAppear:animated];
}
And that all works... right until it doesn't. Sometimes, it works perfectly; other times, either immediately or after a few keystrokes, the application crashes by exiting the main event loop (!). No exception is thrown (at least not that the lldb catches), but the application simply stops, both on the simulator and on hardware.
Any thoughts? Has anyone gotten this working successfully, or knows for sure that it does not?

I think the UIPopoverController instance needs to be a property in your code.

Related

Switch between ViewControllers using a button?

I am very new to programming, especially iOS, so any answer should be given as if I am a baby with ADHD.
This is what I have in my buttonclick:
ItemCreateViewController *itemCreateViewConrtoller = [[ItemCreateViewController alloc] init];
[[self navigationController] pushViewController:itemCreateViewConrtoller animated:YES];
What am I missing?
There are a lot of reasons why this could be happening among which is your button is not triggering the intended action. Among the reason why a button could not trigger is your XIB button is not connected to the button object defined in your header and method file.
If you look at the .h file look at all your IBOutlet items there should be a circle on the left most column if it is darkened out it is connected other wise it is not, and you need to connect this in your XIB file.
If this is not the problem please display the code where you specify the selector it should be something like this:
[cancel_button addTarget:self action:#selector(Cancel:) forControlEvents:UIControlEventTouchUpInside];
Make sure that the selector is an IBAction function -
-(IBAction)Cancel:(id)sender {
[self dismissViewControllerAnimated:YES completion:NULL];
return;
}
One other thing, any statement after pushViewController will get executed prior to any actual change in the view. The actual View change only happens once control is returned to IOS. This is significant if your push view controller command is within a long conditional process and you fail to code the return / exit properly.
Since you are using a XIB file for your second view, you need to create it as follows -
ItemCreateViewController *itemCreateViewConrtoller = [[ItemCreateViewController alloc] initWithNibName:#"ItemCreateViewController" bundle:nil]; //Ensure you use correct nib file name
Then you can push it
[[self navigationController] pushViewController:itemCreateViewConrtoller animated:YES];
But since you are using a Storyboard you could have just added the new view to your storyboard, linked the button to the new view with a "push" segue and everything would have been done for you.

addSubview not calling QLPreviewControllerDataSource instances when offline

I have the following piece of code to add a QLPreviewController subview
{
QLPreviewController *preview = [[QLPreviewController alloc] init];
preview.delegate = self;
preview.dataSource = self;
[self addChildViewController:preview];
[self.view addSubview:preview.view];
[preview didMoveToParentViewController:self];
self.previewController = preview;
}
-(NSInteger) numberOfPreviewItemsInPreviewController:(QLPreviewController *)controller
{
return 1;
}
-(id) previewController:(QLPreviewController *)previewController previewItemAtIndex:(NSInteger)index
{
return self.url;
}
self.url is an NSURL that is located in NSTemporaryDirectory - file://localhost//.../blah.pdf
My issue is that when my laptop is connected to the internet, the document shows up as a subview, but when my laptop is not connected, numberOfPreviewItemsInPreviewController & previewItemAtIndex do not get called.
I've tried a vanilla program with a simple view controller, and it seemed to work fine. (My app is more complex than that).
When I try presenting the document as a modal view, it seems to work irrespective of whether or not the simulator is connected to the internet.
[self presentViewController:preview animated:NO completion:nil]; --> works consistently.
I need to get the subview working for online & offline modes, it would be great if someone could help!
You might be encountering strange behaviour because the QLPreviewController's view is not designed to be embedded in another view. From the QLPreviewController class reference overview:
To display a Quick Look preview controller you have two options: You can push it into view using a UINavigationController object, or can present it modally, full screen, using the presentModalViewController:animated: method of its parent class, UIViewController.
Having said that, you could try:
Forcing the QLPreviewController to (re)display its contents. Try adding [self.previewController reloadData]; to the end of your first method. This should force the data source method(s) to fire.
Forcing the view to "refresh" it's subviews: [self.view setNeedsLayout] (which may in fact force a reloadData like the first option).
Good luck!

newly created UIViewController is not available in a storyboard

I'm running into an issue with one of my view controllers and am hoping someone can help me out: my storyboard behaves as if the view controller does not exist, preventing me from making connections to the controller.
I've created a custom freeform view controller in a storyboard, it's purpose is to be displayed in a popover, allowing a user to edit a UITextLabel using the popover's UITextView.
I'm dynamically instantiating the view controller for the popover using the code below:
//create a popover with a text view under a label to be edited,
// allowing the user to edit the label with the popover
GLAppDelegate* appDelegate = (GLAppDelegate*)[[UIApplication sharedApplication] delegate];
UIViewController *mvc = (UIViewController *)appDelegate.window.rootViewController;
PopoverTextViewController* textViewController = [mvc.storyboard instantiateViewControllerWithIdentifier:#"popoverTextViewController"];
textViewController.textView.text = ((OutlinedLabel*)v.attributedView).text;
textViewController.label = (OutlinedLabel*)v.attributedView;
[self.popover dismissPopoverAnimated:YES];
self.popover = [[UIPopoverController alloc] initWithContentViewController:textViewController];
[self.popover presentPopoverFromRect:v.attributedView.frame inView:v permittedArrowDirections:UIPopoverArrowDirectionUp|UIPopoverArrowDirectionDown animated:YES];
It appears to me that the view controller I made is a typical view controller:
The problem that I'm running in is that autocomplete behaves as if there is no such view controller available in my app. Even if I manually paste it's name here, I cannot connect the textView property from the storyboard to the view controler.
When I test the code, it works - the label can be edited with the popover, but I cannot set the text within the popover, because I cannot link the text view to the view controller.
I appreciate your help!
I figured out the answer, it is a composite, 3 piece thing.
I did a "clean" on the project, then quit xCode and restarted it - this clears some weird bugs, and did work in this case. I was able to add the controller in storyboard
I forgot IBOutlet next to textview: #property(nonatomic,strong)IBOutlet UITextView* textView;
In case of popover, the textView property is nil, until viewDidLoad gets called after the popover is presented, so the code had to be reworked as follows:
//create a popover under the label with a text view, allowing the user to edit the label with the popover
GLAppDelegate* appDelegate = (GLAppDelegate*)[[UIApplication sharedApplication] delegate];
UIViewController *mvc = (UIViewController *)appDelegate.window.rootViewController;
PopoverTextViewController* textViewController = [mvc.storyboard instantiateViewControllerWithIdentifier:#"popoverTextViewController"];
[self.popover dismissPopoverAnimated:YES];
self.popover = [[UIPopoverController alloc] initWithContentViewController:textViewController];
//before this call, all views of the controller are not loaded
[self.popover presentPopoverFromRect:v.attributedView.frame inView:v permittedArrowDirections:UIPopoverArrowDirectionUp|UIPopoverArrowDirectionDown animated:YES];
//once popover is presented, the textView is created and is available
textViewController.textView.text = ((OutlinedLabel*)v.attributedView).text;
textViewController.label = (OutlinedLabel*)v.attributedView;
I hope someone finds this useful.

Controls not responding in UIPopoverController on initial load but do on subsequent loads?

I have an app that is loading a overlay controller (shows camera so I can scan). It works great on the iPhone and it works great on the iPad after I call it a second time. Let me explain.
I have a UIButtonBarItem that loads a view controller modally. There are several controls on in the controller, most buttons (defined using a nib). If I load the controller (by responding to the UIButtonBarItem action) on an iPhone, it loads and all the buttons work fine, every time.
But... if I load the same view controller using an UIPopoverController, none of the buttons will respond the first time I load it. So, I touch the screen somewhere outside of the controller and dismiss the controller. Then, I touch the same action button again and now when the controller loads, all the controls in the the view controller work great. REALLY WEIRD!
[POSSIBLE HINT]
The buttons were placed all over the place in weird positions when I loaded it the first time. Each subsequent call had the buttons showing in the right places. I got this to work by disabling "Autoresize subviews" in the nib. The buttons are now in the right places but they still won't respond when I load this popover the first time.
Here's the code I'm using to respond to the UIButtonBarItem.
-(void)launchOverlayController:(id)sender
{
if([pickerControllerPopover isPopoverVisible])
{
[pickerControllerPopover dismissPopoverAnimated:YES];
pickerControllerPopover = nil;
return;
}
// Deselect any selected cell
[self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:NO];
// Working code that shows the overlay (camera on) but the overlay takes the whole screen
SRSScanVINViewController *scanVINViewController = [[SRSScanVINViewController alloc] init];
[pickerController setOverlay:scanVINViewController];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:scanVINViewController];
[navController setModalPresentationStyle:UIModalPresentationFormSheet];
[navController setModalTransitionStyle:UIModalTransitionStyleFlipHorizontal];
[[UIApplication sharedApplication] setStatusBarHidden:YES];
if([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
pickerControllerPopover = [[UIPopoverController alloc] initWithContentViewController:pickerController];
[pickerControllerPopover setDelegate:self];
[pickerControllerPopover setPopoverContentSize:CGSizeMake(320.0f, 460.0f)];
[pickerControllerPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES];
}
else
{
[self presentViewController:pickerController animated:YES completion:nil];
}
}
I'm totally out of ideas. I can't see why the controls within the overlaycontroller would work fine every time I call it except for the first time.
Thanks for anyones help in advance.
So the answer is that the superclass is mucking with your view. I'm going to guess it was not designed to be subclassed, but no way to know for sure. What it does in one of the 'view..' methods is to override self.view with its own view, and make your view a subview of that view. The first time around it makes the frame of your view have zero dimensions. The next time it leaves it as it was before - maybe some persistent flag. It also inserts the view at different places in its subviews, which seems odd but if you have the code you'd probably see why.
Soooo - the solution to the problem is to just move your view's subviews to the superView (the subclass's view), then set your view's frame to the null frame:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated]; // StackOverflow says to add this TCL?
// Set the initial scan orientation
[self setLayoutOrientation:self.parentPicker.orientation];
if ([self.parentPicker hasFlash])
{
[flashButton setEnabled:YES];
[flashButton setStyle:UIBarButtonItemStyleBordered];
[self.parentPicker turnFlash:NO];
} else
{
[flashButton setEnabled:NO];
}
textCue.text = #"";
viewHasAppeared = NO;
// move the subviews
while([self.view.subviews count]) [self.view.superview addSubview:[self.view.subviews objectAtIndex:0]];
self.view.frame = CGRectMake(0,0,0,0);
}
PS: note that you were missing a superView call here but it didn't seem to matter much (you don't know which method your complex superclass may want so I'd be sure to send them everything you intercept.

removefromsuperview and addsubview not changing display

I'm pretty new to iOS development, and I'm trying to develop a simple app in which a button changes the subviews. I have a base RootViewController, which displays MiddleView correctly on init. MiddleView has a single button, labeled "First," which is connected (in Interface Builder) to RootViewController's -openFirstView.
Here's how MiddleView is displayed within RootViewController's -viewDidLoad
MiddleViewController *middleTemp = [[MiddleViewController alloc] initWithNibName:#"MiddleView" bundle:nil];
self.middle = middleTemp;
self.middle.rootViewController = self;
[self.view addSubview:middle.view];
[middleTemp release];
So I have the following ViewControllerss: MiddleViewController and FirstController which control MiddleView and FirstView respectively, and a RootViewController which switches between the two.
I've linked this by placing a RootViewController reference in MiddleViewController, and adding
self.middle.rootViewController = self;
to RootViewController's -viewDidLoad.
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
[self.view addSubview:firstController.view];
}
Note: I've tried initializing firstController within -openFirstView, and when it initially didn't run, I moved the initialization to -viewDidLoad and have proven that it is initializing from the nib correctly by displaying FirstView directly in -viewDidLoad
Where firstController is loaded to a reference earlier in code. However, when I run the code and click the button, nothing in the view changes.
I've done some more diagnosing. I've found specifically that -ViewDidLoad in rootViewController is being called twice, once on the original load and once on the first click of the button, and I'm not sure exactly why.
It seems you haven't inialize your firstView in the below method
-(IBAction)openFirstView:(id)sender{
[middle.view removeFromSuperview];
//First initialize the firstController
[self.view addSubview:firstController.view];
}

Resources