iOS Subviews added programmatically are lost when view is reloaded from the storyboard - ios

I have an app with two view controllers. ViewControllerA is a blank view with a tap gesture assigned which allows the user to tab on the view and create a UITextView at the point of the tap. The user can create as many UITextViews as they wish and they are added then programmatically to the view (ViewControllerA) as sub views.
There is also a button which allows the user to change the text font and styling. This triggers a Segue to the second view controller ViewControllerB which then allows the user to set Font, Text Size, Color etc. Once completed the user clicks the DONE button on ViewControllerB and another Segue switches back to the initial view (ViewControllerA).
This all works fine. Except when the user switches back to the initial view (ViewControllerA) from ViewControllerB the view is reloaded from the storyboard and the sub views I have added programmatically are gone.
In view (ViewControllerA) ViewDidLoad and ViewWillAppear are firing just fine so the problem seems to be the initial view is released when the first Segue fires and then recreated from the storyboard on the transition back but the subviews are of course not included as they are not in the storyboard since I added them programmatically.
Any suggestions for a best practice on how to solve this so that the subviews are recreated also when the main view (ViewControllerA) reloads?
Many thanks for any suggestions!

From the question it sounds like you had a segue to the text styles view, then another segue "back to the original" - it doesn't work like that, segues always make new instances of the destination VC. You should have had a modal segue to the text styles view, then dismissed the modal view controller - this would return to your original instance.

Just for the record, I solved this as follows in case anyone else needs a solution.
I created a subview in ViewControllerA which is the size of the main view excluding the Toolbar. I call this canvasView. Then I add all of my ImageViews and TextViews to this canvas view.
Then in ViewControllerA viewWillDisappear I archive the canvasView and all of its subviews to a file like this.
NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"Canvas.archive"];
BOOL result = [NSKeyedArchiver archiveRootObject:_canvasView
toFile:archivePath];
if (!result) {
NSLog(#"Archive failed to archivePath %#",archivePath);
}
Then in ViewControllerA viewWillAppear I check if there is an existing archive and if so reload it which loads the sub views in the correct order. Otherwise I create an empty canvasView like this.
// If the collageView already exists then restore it from the achive, otherwise initialize a new one.
NSString *archivePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"Canvas.archive"];
_canvasView = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
if (_canvasView) {
// Restore existing canvasView
[_backgroundView addSubview:_canvasView];
} else {
// Initialize a new canvasView
_canvasView = [[UIScrollView alloc] initWithFrame:CGRectMake(_backgroundView.frame.origin.x,
_backgroundView.frame.origin.y,
_backgroundView.frame.size.width,
_backgroundView.frame.size.height)];
[_backgroundView addSubview:_canvasView];
}

Related

how do I display different views in a view controller using a tab bar

I have a view controller that has a map view and a second view under the a tab bar. How do I go about updating the second view when I press buttons on the tab bar?
I tried:
LocationNotesViewController lnvc = new LocationNotesViewController();
lnvc.View.Frame = MainPageTabBarView.Frame;
MainPageTabBarView = lnvc.View;
Nothing happens...the view doesn't update.
I want to update the second view with different things when a user clicks on the tabbar...
If you put a UIView underlying whatever data you want displayed in it, you can use the IBAction of the tabBar to programtically cahnge out the contents of the UIView.
Or you could have the IBActions of the tabbar create new UIViews on the fly, containing whatever you want inside.
Code to do this would be like explained here: http://www.programmerscountry.com/creating-uiviewcontrols-programatically/.
Not exactly the same, but you will understand how it works from that answer.
To switch UIViewControllers use this piece of code:
UIViewController *viewController =
[self.storyboard instantiateViewControllerWithIdentifier:#"4"];
[self presentViewController:a animated:YES completion:nil];
I think this is a work around - but I was able to make this happen by doing:
LocationNotesViewController lnvc = new LocationNotesViewController();
lnvc.View.Frame = new RectangleF(MainPageTabBarView.Frame.X, MainPageTabBarView.Frame.Y - MainPageTabBarView.Frame.Y, MainPageTabBarView.Frame.Width, MainPageTabBarView.Frame.Height);
MainPageTabBarView.AddSubview(lnvc.View);

Display UIViewController as Popup in iPhone

Since there is no complete, definitive answer to this common recurring question, I'll ask and answer it here.
Often we need to present a UIViewController such that it doesn't cover full screen, as in the picture below.
Apple provides several similar UIViewController, such as UIAlertView, Twitter or Facebook share view controller, etc..
How can we achieve this effect for a custom controller?
NOTE : This solution is broken in iOS 8. I will post new solution ASAP.
I am going to answer here using storyboard but it is also possible without storyboard.
Init: Create two UIViewController in storyboard.
lets say FirstViewController which is normal and SecondViewController which will be the popup.
Modal Segue: Put UIButton in FirstViewController and create a segue on this UIButton to SecondViewController as modal segue.
Make Transparent: Now select UIView (UIView Which is created by default with UIViewController) of SecondViewController and change its background color to clear color.
Make background Dim: Add an UIImageView in SecondViewController which covers whole screen and sets its image to some dimmed semi transparent image. You can get a sample from here : UIAlertView Background Image
Display Design: Now add an UIView and make any kind of design you want to show. Here is a screenshot of my storyboard
Here I have add segue on login button which open SecondViewController as popup to ask username and password
Important: Now that main step. We want that SecondViewController doesn't hide FirstViewController completely. We have set clear color but this is not enough. By default it adds black behind model presentation so we have to add one line of code in viewDidLoad of FirstViewController. You can add it at another place also but it should run before segue.
[self setModalPresentationStyle:UIModalPresentationCurrentContext];
Dismiss: When to dismiss depends on your use case. This is a modal presentation so to dismiss we do what we do for modal presentation:
[self dismissViewControllerAnimated:YES completion:Nil];
Thats all.....
Any kind of suggestion and comment are welcome.
Demo :
You can get demo source project from Here : Popup Demo
NEW : Someone have done very nice job on this concept : MZFormSheetController
New : I found one more code to get this kind of function : KLCPopup
iOS 8 Update : I made this method to work with both iOS 7 and iOS 8
+ (void)setPresentationStyleForSelfController:(UIViewController *)selfController presentingController:(UIViewController *)presentingController
{
if (iOSVersion >= 8.0)
{
presentingController.providesPresentationContextTransitionStyle = YES;
presentingController.definesPresentationContext = YES;
[presentingController setModalPresentationStyle:UIModalPresentationOverCurrentContext];
}
else
{
[selfController setModalPresentationStyle:UIModalPresentationCurrentContext];
[selfController.navigationController setModalPresentationStyle:UIModalPresentationCurrentContext];
}
}
Can use this method inside prepareForSegue deligate like this
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
PopUpViewController *popup = segue.destinationViewController;
[self setPresentationStyleForSelfController:self presentingController:popup]
}
Modal Popups in Interface Builder (Storyboards)
Step 1
On the ViewController you want as your modal popup, make the background color of the root UIView clear.
Tip: Do not use the root UIView as your popup. Add a new UIView that is smaller to be your popup.
Step 2
Create a Segue to the ViewController that has your popup. Select "Present Modally".
Two Methods To Create Popup From Here
Method One - Using the Segue
Select the Segue and change Presentation to "Over Current Context":
Method Two - Using the View Controller
Select the ViewController Scene that is your popup. In Attributes Inspector, under View Controller section, set Presentation to "Over Current Context":
Either method will work. That should do it!
You can do this in Interface Builder.
For the view you wish to present modally set its outermost view background to transparent
Control + click and drag from the host view controller to the modal view controller
Select present modally
Click on the newly created segue and in the Attribute Inspector (on the right) set "Presentation" to "Over Current Context"
Feel free to use my form sheet controller MZFormSheetControllerfor iPhone, in example project there are many examples on how to present modal view controller which will not cover full window and has many presentation/transition styles.
You can also try newest version of MZFormSheetController which is called MZFormSheetPresentationController and have a lot of more features.
You can use EzPopup (https://github.com/huynguyencong/EzPopup), it is a Swift pod and very easy to use:
// init YourViewController
let contentVC = ...
// Init popup view controller with content is your content view controller
let popupVC = PopupViewController(contentController: contentVC, popupWidth: 100, popupHeight: 200)
// show it by call present(_ , animated:) method from a current UIViewController
present(popupVC, animated: true)
Imao put UIImageView on background is not the best idea . In my case i added on controller view other 2 views . First view has [UIColor clearColor] on background, second - color which u want to be transparent (grey in my case).Note that order is important.Then for second view set alpha 0.5(alpha >=0 <=1).Added this to lines in prepareForSegue
infoVC.providesPresentationContextTransitionStyle = YES;
infoVC.definesPresentationContext = YES;
And thats all.
Swift 4:
To add an overlay, or the popup view
You can also use the Container View with which you get a free View Controller ( you get the Container View from the usual object palette/library)
Steps:
Have a View (ViewForContainer in the pic) that holds this Container View, to dim it when the contents of Container View are displayed. Connect the outlet inside the first View Controller
Hide this View when 1st VC loads
Unhide when Button is clicked
To dim this View when the Container View content is displayed, set the Views Background to Black and opacity to 30%
You will get this effect when you click on the Button
You can do this to add any other subview to the view controller.
First set the status bar to None for the ViewController which you want to add as subview so that you can resize to whatever you want. Then create a button in Present View controller and a method for button click. In the method:
- (IBAction)btnLogin:(id)sender {
SubView *sub = [[SubView alloc] initWithNibName:#"SubView" bundle:nil];
sub.view.frame = CGRectMake(20, 100, sub.view.frame.size.width, sub.view.frame.size.height);
[self.view addSubview:sub.view];
}
Hope this helps, feel free to ask if any queries...

Present a UIScrollView when opening an App for the first time

I have an iPad App that I want to be compatible from iOS 5.0 to 6.0. My main view contains a scroll view z-indexed on the front, which is initially set to hidden. I also have a toolbar containing a button that cycle the scroll view hidden or not.
I would like to add a feature to present the scroll view as initially visible when the user opens the App for the first time to make the help visible by default to new users.
My code to cycle between visible and hidden is the following:
- (void)showHelpView:(id)sender {
BOOL hidden = [blackTranslucent isHidden];
[self.view bringSubviewToFront:scrollViewOutlet];
if (hidden) {
[scrollViewOutlet setHidden:FALSE animationStyle:KGAnimationFade duration:0.7];
[blackTranslucent setHidden:FALSE animationStyle:KGAnimationFade duration:0.5];
}
else {
[scrollViewOutlet setHidden:TRUE animationStyle:KGAnimationFade duration:0.5];
[blackTranslucent setHidden:TRUE animationStyle:KGAnimationFade duration:0.7];
}
}
where the sender is my toolbar button, blackTranslucent is a view on top of the main view and scrollViewOutlet is my scroll view IBOutlet.
Add a property "isNewUser" on NSUserDefaults in the application:willFinishLaunchingWithOptions: method which will only be written once by checking if the key exists.
In your main view in the viewDidLoad check this property if true make the view visible and update the key to false. if not just continue regularly.
Further information on NSUserDefaults
Hope that helps

popover content view doesn't display while viewcontroller has a child VC present

I have a container view controller that consists of a navigation view at top, and a content view for the remainder of the screen. The navigation menu consists of several buttons, some of which present a popover with UITableView for secondary navigation. This all worked until I assigned a child view controller and set it's view as subview of the content view. Now, the popover appears, but has nothing inside it (no tableview, just black).
Why is this?
Here's the code I added for the child vc in container view:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
ContentWebViewController *initialVC = [[ContentWebViewController alloc] init];
[self addChildViewController:initialVC];
initialVC.view.frame = self.view.bounds;
[self.containerView addSubview:initialVC.view];
self.currentController = initial;
}
See the screenshot below. I added a vc with a simple webview showing google (just as a placeholder for now). The popover was working fine before I assigned the child VC.
Maybe it will help other in other cases -
If you are using size classes (probably you are since you are developing this to iPad) -
Design your popover view controller in Any-Any size and it should be OK - after that you can return to your wanted size.
(You can also uninstall the size classes of any object in that view controller instead of redesign the VC)
I somehow (don't ask me how) changed the class that my table view controller was inheriting from. It should have been (obviously) UITableViewController, but was UITableViewController, so initWithStyle was not being called....

iOS storyboards

I have a iOS single-view application for the iPad and using storyboards.
I have the default viewcontroller created by xcode (vc1). I
place a button on its view.
I then drag a new view controller (screen2VC) onto the
storyboard.
I also create a new view controller subclass (vc2SubClass)
I associate screen2VC to this subclass via the identity inspector.
On screen2VC's view, I have a label. I control-drag the label onto
vc2SubClass to create an outlet.
I create a segue between vc1's button and vc2's button.
In performSegue override i have the following code:
if ([segue.identifier isEqualToString:#"segue1"])
{
screen2VC* vc = [segue destinationViewController];
vc.label1.text = #"screen 2";
}
When I run the code and press the button, the segue works and transitions between the two views just fine, but screen2VC's label is never set to 'screen 2'. As a test, I placed the label1.text = #"screen 2"; code into vc2SubClass, but that made no difference. It seems there's still no association between screen2VC and vc2SubClass, even though the former is subclassing the latter and the label is pointing to a UILabel outlet.
Any ideas?
When you're in performSegue: of vc1, vc2's view hierarchy has not yet been set up. The normal way of doing this is to have a property in vc2 that temporarily is given the string during performSegue:. This property is then used to update the label in vc2's viewWillAppear:.
(When you say "As a test, i placed the 'label1.text = #"screen 2";' code in vc2SubClass but that makes no difference" I'm not sure what's going on there. What method was that code put into?)

Resources