I hope you can help me with the next case.
Description:
I have a class that "creates" a dynamic form programmatically, this class inherits from UIViewController and is called "DinamicScreen".
#interface DinamicScreen : UIViewController ...
I have a ViewController (connected to a viewController storyboard), which inherits from "DinamicScreen".
#interface MandatoryInformationViewController : DinamicScreen<UIPickerViewDelegate,UITextFieldDelegate>
In "DinamicScreen" I'm only painting the UIView, but all delegates are passed to the context.
uiTextField = [[UILabel alloc]initWithFrame:....
// Context is "MandatoryInformationViewController" in this case
[uiTextField setDelegate:context];
The form implements a UIScrollView containing UILabels and UITextFields. An UITextField can open an ActionSheet showing an UIPickerView and two buttons (OK, Cancel).
DinamicScreenScrollView *uiScrollView = [[DinamicScreenScrollView alloc]initWithFrame...
[uiScrollView addSubview:uiTextField];....
The problem:
When I click on a "pickerTextField" the ActionSheet appears without problems, but when I click OK or Cancel, it crashes (I think it is the ActionSheet).
When I did a debug, I observed that the flow does not run correctly
[actionSheet dismissWithClickedButtonIndex:0 animated:YES];
The snapshot http://s29.postimg.org/8zxkj333b/Captura_de_pantalla_2013_12_17_a_la_s_17_45_38.png
If I do not implement the UIScrollView it works perfectly, but its a must for it to be implemented.
If more information is needed please let me know, it's my first question on StackOverflow
Excuse me for my English, I'm working on it.
Thanks.
Well, after three days to fix it.
The problem was that were painting in the view of "MandatoryViewController" instead of "DinamicScreen", I changed that and voila.
[[context view] addSubview:uiScrollView]; // [wrong]
[[self view] addSubview:uiScrollView]; // [well]
I supposed that this post are a documentation now.
Thanks again.
Related
I am new to iOS development and I am currently reading the book : iOS Programming (Objective C) by Big Nerd Ranch.
I am confused as in where to initialize subviews such as UIButtons, UIImageView while creating views programtically:
Should the intialization be done in the Main UIView i.e in the
initWithFrame method and maintain a additional weak reference to the subview in the UIView.
or
should I do it in the UIViewControllers loadView method and maintain a weak reference to the subview in the uiviewcontroller (Same approach used while creating UIVew using the interface builder).
I have seen both the approaches being used in various stackoverflow posts but no post that explains which approach is the right one.
you can initialize as per your app's requirement. If any view or button or anything is part of initial setup of your app then you should initialize it in viewDidload.
Now, for example there is requirement like user press button and then new view will be created then you can initialize view in button's click method etc.
So, it's depends on your requirement.
Static views which will live from start to and of app should be initialize in viewdidload, because this is the first method getting called of viewcontroller.
hope this will help :)
It dependes on which architecture you are using. Apple raises the flag of Model-View-Controller, but in fact, UIViewControllers are the View.
For Example:
Let's say that you have a pretty LoginViewController. When you instantiate it, you will be doing something like
LoginViewController *loginVC = [[LoginViewController alloc] init];
At this point, no view is loaded. Your ViewController has just executed the init method, nothing else. When the system calls
loginVC.view
the first method to be executed will be
- (void)loadView;
there you should do exactly that, load your view. So, the approach i like is to have an additional LoginView.
- (void)loadView
{
// you should have a property #property (nonatomic, strong) LoginView *loginView;
self.loginView = [[LoginView alloc] init];
self.view = self.loginView;
}
and in the LoginView init method, you should put your code to build up the view.
However, you could eliminate LoginView, and instantiate all your subviews like this:
- (void)loadView
{
self.view = [[UIView alloc] init];
UIButton *button = [[UIButton alloc] initWithTargetBlaBlaBla...];
[self.view addSubview:button];
// add more fancy subviews
}
In my experience, the first approach is much cleaner than the second one. It also makes version control a lot easier (try to merge a xib, I dare you). I always use MyView.m to build the view (a.k.a setup constriants, style) and use MyViewController.m things like animations, lifeCycle. I like to think that MyView.m is the programatic xib, so anything that you can do with xibs, you should me able to do it inside your view.
Hope it helps!!
I have two View Controllers: SavePopOverVC and MainVC. I also have a nib file called SavePopOver. SavePopOver has three items, a UIButton, a UIImage and a UITextView. The image and text view have outlets to property fields in SavePopOverVC called captionImage and captionTextView respectively. The button has an outlet to an IBAction in SavePopOverVC.
In MainVC.m I have the following two lines in my class extension.
SavePopOverVC *spvc;
UIPopoverController *popover;
In my viewDidLoad of the same file I have the following lines relating to my popover.
spvc = [[SavePopOverVC alloc] initWithNibNamed:#"SavePopOver" bundle:nil];
popover = [[UIPopoverController alloc] initWithContentViewController:spvc];
In my function that displays my popover, also in MainVC.m, I have the following lines.
[popover setPopoverContentSize:CGSizeMake(600,200)];
[popover presentPopoverFromRect:_header.frame inView:self.view permittedArrowDirection:UIPopoverArrowDirectionAny animated:YES];
[((SavePopOverVC *)popover.contentViewController).captionTextView setText:#"Some text here"];
However, captionTextView is nil when I make the setText: call. The app doesn't crash but the text isn't set. After the popover is displayed and I click on the UIButton to save the string typed in captionTextView I get the string just fine. So, I know the two are ultimately linked correctly, but how can I set captionTextView from when I display the popover?
If it is worth noting, I'm developing solely for iPad with this one.
It is most likely nil because its view isn't loaded at the time you set the text. Unlike most other modern languages, in Objective-C calling a method on a nil object doesn't cause an exception, it just does nothing.
To solve this, you can create a custom NSString property in your SavePopOverVC, e.g.
#property (nonatomic, copy) NSString *caption;
Before you call presentPopoverFromRect:, assign a value to this property. Inside SavePopOverVC, override viewDidLoad and set the captionTextView.text = self.caption;
There might be people who disagree with me, but I don't recommend exposing UI controls as properties in a view controller. This behaviour is one of the reasons for that.
I know that this question was asked before many times but I didn't find the solution for my problem. I am not new at iOs and this should be pretty trivial stuff, but it is driving me mad and crazy :). Ok I have class C that is subclass of UIViewController. In it'S view there is a UIScrollView and in that scroll view I put class B and A also subclasses of UIViewController and I do it like this:
a = [[A alloc] initWithNibName:#"A" bundle:nil];
[a setDelegate:self];
[self addChildViewController:a];
[a.view setFrame:CGRectMake(2*vwMainScroller.frame.size.width, 0, a.view.frame.size.width, a.view.frame.size.height)];
[vwMainScroller addSubview:a.view];
b = [[B alloc] initWithNibName:#"B" bundle:nil];
[b setDelegate:self];
[self addChildViewController:b];
[b.view setFrame:CGRectMake(vwMainScroller.frame.size.width, 0, b.view.frame.size.width, speedScreen.view.frame.size.height)];
[vwMainScroller addSubview:b.view];
Both class A and class B have buttons on their views added programatically in viewDidLoad method like this:
- (void)viewDidLoad
{
[super viewDidLoad];
UIButton *btnChangeColor = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 200, 460)];
[btnChangeColor addTarget:self action:#selector(Btn) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btnChangeColor];
}
-(void)Btn
{
NSLog(#"I am the selector and I have been called");
}
But when I press class A button the selector is never called, but when I press class B button the selector is called just like it should be.
I tried:
putting buttons on xibs
enabling user interaction for every view in my code - it was already enabled -> didn't help
painting buttons to see if they are where they should be -> they were
restarting computer and xCode
running on device and simulator
Nothing helped, always the same thing, button works on class B but not on class A, after I tried to add some other buttons in IB of class A, after I found out that they also don't call their selectors I checked the box "Shows touches on highlight" and guess what, they didn't glow when I touched them. So please I see that something is eating my touch events but just don't have any ideas how to proceed in debugging this matter. Please help me :)...
Try set up vwMainScroller.delaysContentTouches = Yes on your scroll view.
Add
[a.view setUserInteractionEnabled:YES];
and
[b.view setUserInteractionEnabled:YES];
before you add it as a subview to your scrollView, If it doesn't help set up your view controller as a delegate of your UIScrollView and add this:
-(BOOL)touchesShouldCancelInContentView:(UIView *)view
{
return ![view isKindOfClass:[UIButton class]];
}
Ok, stop thinking, cause that is what U need to do to solve this problem. I stopped thinking and created a new class "Anew", copied all code from class A, created exactly the same xib, by copying elements from A.xib, deleted class A and implemented Anew in exactly the same way I did with class A. And now everything is working as it should. So the answer is when U spend 4 hours on trying to solve something as trivial as this, and u tried every possible approach that U can imagine and that others suggested, and nothing helps, it is time to stop thinking and start deleting cause the problem is probably deeper than U can reach...
Is there a way to add the tableView portion of a UITableViewController to a UIView?
Here is what I've got...
1) UIViewController - MainViewController
2) UIView - customUIView - Added to MainViewController (This UIView is replaced when a new page is loaded)
When a button is clicked all of the other pages in my app are loaded using (example)
mainViewController.customUIView = [[LoginPage alloc] initWithFrame:mainViewController.view.bounds];
3) UITableViewController - ScoresNotificationsPage - Here is my issue, trying to add the tableView portion of this into the UIView and have it display on the MainViewController, just as if any other UIView was added.
When I try loading the tableView portion of the UITableViewController into customUIView using:
mainViewController.customUIView = [[ScoresNotificationsPage alloc] initWithFrame:mainViewController.view.bounds];
I get the following error
Incompatible pointer types assigning to 'UIView *' from 'ScoresNotificationsPage *'
Any idea of what I could do to get the tableView to show up?
Thank you in advance.
Whew...
Make ScoresNotificationsPage a child view controller and then simply add it: [self.view addSubview:scoresPage.tableView]?
I have a UIActivity subclass that creates its own activityViewController:
- (UIViewController *)activityViewController {
WSLInProgressViewController* progressView = [[[WSLInProgressViewController alloc] init] autorelease];
progressView.message = [NSString stringWithFormat:NSLocalizedString(#"Posting to %#...",#"Posting to..."),
self.activityType];
return progressView;
}
I've add a full repro on GitHub.
According to the documentation, you aren't supposed to dismiss this manually. Instead, the OS does that when you call activityDidFinish:. This works fine when ran on an iPhone.
When I say "works," this is the sequence of events that I'm expecting (and see on the iPhone):
Display the UIActivityViewController
User presses my custom activity
My view controller appears
I call activityDidFinish:
My custom view controller is dismissed
The UIActivityViewController is also dismissed
However, when I run this same code on the iPad Simulator -- the only difference being that I put the UIActivityViewController in a popup, as the documentation says you should -- the activityViewController never dismisses.
As I say, this is code wo/the popUP works on the iPhone and I have stepped through the code so I know that activityDidFinish: is getting called.
I found this Radar talking about the same problem in iOS6 beta 3, but it seems such fundamental functionality that I suspect a bug in my code rather than OS (also note that it works correctly with the Twitter and Facebook functionality!).
Am I missing something? Do I need to do something special in the activityViewController when it's run in a UIPopoverViewController? Is the "flow" supposed to be different on the iPad?
The automatic dismissal only appears to happen when your 'activity' controller is directly presented, not wrapped in anything. So just before showing the popup it's wrapped in, add a completion handler
activity.completionHandler = ^(NSString *activityType, BOOL completed){
[self.popup dismissPopoverAnimated:YES];
};
and you'll be good.
I see the question is quite old, but we've been debugging the same view-controller-not-dismissing issue here and I hope my answer will provide some additional details and a better solution than calling up -dismissPopoverAnimated: manually.
The documentation on the UIActivity is quite sparse and while it hints on the way an implementation should be structured, the question shows it's not so obvious as it could be.
The first thing you should notice is the documentation states you should not be dismissing the view controller manually in anyway. This actually holds true.
What the documentation doesn't say, and what comes as an observable thing when you come across debugging the non-dissmissing-view-controller issue, is iOS will call your -activityViewController method when it needs a reference to the subject view controller. As it turns out, probably only on iPad, iOS doesn't actually store the returned view controller instance anywhere in it's structures and then, when it wants to dismiss the view controller, it merely asks your -activityViewController for the object and then dismisses it. The view controller instantiated in the first call to the method (when it was shown) is thus never dismissed. Ouch. This is the cause of the issue.
How do we properly fix this?
Skimming the UIActivity docs further one may stumble accross the -prepareWithActivityItems: method. The particular hint lies along the following text:
If the implementation of your service requires displaying additional UI to the user, you can use this method to prepare your view controller object and make it available from the activityViewController method.
So, the idea is to instantiate your view controller in the -prepareWithActivityItems: method and tackle it into an instance variable. Then merely return the same instance from your -activityViewController method.
Given this, the view controller will be properly hidden after you call the -activityDidFinish: method w/o any further manual intervention.
Bingo.
NB! Digging this a bit further, the -prepareWithActivityItems: should not instantiate a new view controller each time it's called. If you have previously created one, you should merely re-use it. In our case it happily crashed if we didn't.
I hope this helps someone. :)
I had the same problem. It solved for me saving activityViewController as member and return stored controller. Activity return new object and dismiss invoked on new one.
- (UIViewController *)activityViewController {
if (!self.detaisController) {
// create detailsController
}
return self.detailsController;
}
I pass through the UIActivity to another view then call the following...
[myActivity activityDidFinish:YES];
This works on my device as well as in the simulator. Make sure you're not overriding the activityDidFinish method in your UIActivity .m file as I was doing previously. You can see the code i'm using here.
a workaround is to ask the calling ViewController to perform segue to your destination ViewController via - (void)performActivity although Apple does not recommend to do so.
For example:
- (void)performActivity
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
[self.delegate performSomething]; // (delegate is the calling VC)
[self activityDidFinish: YES];
}
}
- (UIViewController *)activityViewController
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
UIViewController* vc=XXX;
return vc;
}
else
{
return nil;
}
}
Do you use storyboards? Maybe in your iPad storyboard, the UIActivityIndicatorView doesn't have a check on "Hides When Stopped"?
Hope it helps!
So I had the same problem, I had a custom UIActivity with a custom activityViewController and when it was presented modally it would not dismiss not matter what I tried. The work around I choose to go with so that the experience remained the same to the user was to still use a custom UIActivity but give that activity a delegate. So in my UIActiviy subclass I have the following:
- (void)performActivity
{
if ([self.delegate respondsToSelector:#selector(showViewController)]) {
[self.delegate showViewController];
}
[self activityDidFinish:YES];
}
- (UIViewController *)activityViewController
{
return nil;
}
Then I make the view controller that shows the UIActivityViewController the delegate and it shows the view controller that you would otherwise show in activityViewController in the delegate method.
what about releasing at the end? Using non-arc project!
[progressView release];
Many Users have the same problem as u do! Another solution is:
UIActivityIndicatorView *progress= [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(125, 50, 30, 30)];
progress.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[alert addSubview:progress];
[progress startAnimating];
If you are using storyboard be sure that when u click on the activityind. "Hides When Stopped" is clicked!
Hope that helped...