How to replace self (UIView) with another UIView? - objective-c

View A has a button upon clicking it, we go to view B
View B does not retain a pointer to view A.
From view B, i'd like to load view A back (programmatically)
Effectively, i'd like to kill B and replace it with A.
I was thinking that the following should work but, it does not
Calling from View B
ViewController *main = [ViewController new];
[self addSubview:[main view]];
What am i missing please?

Personally I think the easiest way to do this would be by having a UIViewController with an IBOutlet to both UIView objects. You can add and design them both in the interface builder and just set one of them (view B) as hidden (it's a property in UIView).
Then, you could specify a button action to toggle the visibility of view B.
I must add though that there are constructs for implementing screen flows, such as the NavigationController. In your case, however, you might also consider the use of the presentModalViewController:animated: method.
It all depends really, but in general it's better practice to make a seperate UIViewController for each UIView in your application.
Hope this helps!

This sounds to me like you're looking for a navigation controller. You can easily take away a navigation controllers NavBar and take away the animations if you don't want them - but this would achieve exactly what you're looking for.

Related

Moving between view controllers Objective-C

I was wondering if you could help me. I am developing an app where I have five UIView Controllers, each with fields to be completed and filled in. At the bottom of the page I have created a bottom bar with five buttons, one for each controller.
What I want is the following: If a user has filled in half of lets say VC_1 and then goes to VC_2, fills in a few fields here, and then goes back to VC_1 or a new VC3, that the progress before is still there.
I have tried this using "presentViewController" on each button press, however what happens if I go back to 1 is that all fields are empty, probably because it throws the viewController on top of the current one. One way that the data is not deleted is if I use dismissViewController, however this always brings me back to VC_1 and would not work if I wanted to go back from VC_3 to VC2.
I hope my questions makes sense and that one of you could explain how I can achieve the above objective.
Cheers,
Lennaert
Here are two idea which can help
Create and use models that persist longer than any view controller.
It sounds like you should use a UITabBarController.
For idea 1, whenever you are storing data create a class separate from any view controller to hold the data. When you create an instance your view controller, assign that data object to the view controller. Then when the view loads, use the data in the data object to fill in the fields. When the user makes a change with any of the controls, update the data object. See Model-View-Controller.
For idea 2, using a tab bar controller will keep active instances of all your view controller. That way you will not need to create new instances of them.
Looks like UITabBarController is your friend. It manages showing and hiding of the views for you.
Instead of UITabBarController I think your best choice is a UIScrollView with big contentSize and pagingEnabled = YES. All in one viewController and you can customize the bottom bar as you like.
Hmm Couple things to consider. As Per Allen's comment, you might want to consider embedding your project in a UITabBarController:
Here's how to do it programmatically:
#property (nonatomic, strong) UITabBarController *tabBarController;
MyView * view = [[MyView alloc]initWithNibName:#"MyView" bundle:nil];
view.title = #"My Planner";
nav1 = [[UINavigationController alloc] initWithRootViewController:view];
nav1.tabBarItem.image = [UIImage imageNamed:#"nofill_star.png"];
self.tabBarController.viewControllers=[NSArray arrayWithObjects:nav1,nil];
OR
MyView * view = [[MyView alloc]initWithNibName:#"MyView" bundle:nil];
view.title = #"My Planner";
self.tabBarController.viewControllers=[NSArray arrayWithObjects:view,nil];
Or from your story board, go to editor embed in tab bar. Also check out NSUserDefualts. That's something to consider as well.

UIViewController within a UIViewController

So I have a viewControllerA, and I want to add another View managed by viewControllerB to it. There is only one UISlider activating a simple action in viewControllerB. It won't crash if I don't touch this UISlider, it will once I use UISlider. I am using ARC. I am using:
[self.view addSubView: viewControllerB.view];
to add viewControllerB to viewControllerA. Am I missing something? Thanks.
OK. It looks like a really simple situation. I just added one view controller and one action. Here is the demo project code on github: https://github.com/randomor/Demo
The reason why I want this to work is because I have another app that will create a view controller on the spot and add it to anther view. And I don't want to do it modally, because I don't want the new view controller to cover the whole screen. Thanks.
SOLUTION: So I'm now just using the latest ViewController containment API:
[self addChildViewController:viewControllerB];
It works! as long as I added this line, the event will be passed to its own controller and it stopped crashing.
i recommend you, to use the following code
in ViewControllerA.h
#import "ViewControllerB.h"
in ViewControllerA.m (where you want to push the new controller)
ViewControllerB *newController = [[ViewControllerB alloc]init];
[self presentModalViewController:newController animated:YES];
in ViewControllerB.m you will need
[self.presentingViewController dismissModalViewControllerAnimated:YES];
to make it vanish again.
concerning multiple controllers for one open screen (Apple ViewController Programming Guide):
Each custom view controller object you create is responsible for managing exactly
one screen’s worth of content. The one-to-one correspondence between a view controller
and a screen is a very important consideration in the design of your application.
You should not use multiple custom view controllers to manage different portions
of the same screen. Similarly, you should not use a single custom view controller
object to manage multiple screens worth of content.
You should try and avoid the practice of nesting UIViewControllers. While it is technically supported in iOS5, it is ill-advised, for many reasons, including the type of problem that you're having (you have a dangling pointer to a UIViewController, which is why you are crashing).
http://blog.carbonfive.com/2011/03/09/abusing-uiviewcontrollers/
Although this question is extremely vague, I imagine that you are not keeping a reference to View Controller B, and so when view B tries to interact with it, it causes EXC_BAD_ACCESS.
What's the object that is set as the target for the slider? If it's a EXC_BAD_ADDRESS, then you may not be retaining the target, most probably the view controller for the slider.

How to dismiss a modal that was presented in a UIStoryboard with a modal segue?

Setup: I have a storyboard set up, with two simple view controllers A and B. There is a button in A, that transitions to B with a modal segue. B is presented with a modal transition on top of A. It’s fine.
Question: is there a way to pop B away and get back to A with some simple storyboard magic?
Note that if this was all in a navigation controller, and I used a push segue, it would be implicitly be taken care of by navigation controller. There would be a “back” button. There’s nothing comparable for modals, I need to build the UI myself which is fine, but I am wondering if there is a segue mechanic I can use to signal to go back from B to A.
Now the oldskool method to build going back from B to A would be:
create a delegate property on B
set A to be B's delegate when the modal transition segue plays back (I can hook into this using prepareForSegue:sender: in A’s code)
when it’s time to dismiss, B signals to its delegate
A implements a delegate method that dismisses B
This works, but feels like too much overhead and silly.
Is there some UIStoryboard mechanic that I have missed, that would basically do a “reverse modal segue”?
There isn't any storyboard magic for dismissing a modal view controller without writing at least a little bit of code.
But while you do have to implement some code of your own, you don't necessarily have to go to that much trouble. You can just have a button in view controller B that calls [self dismissViewControllerAnimated:YES completion:nil]. (The docs say the presenting view controller should be the one to dismiss, but they also say that the message will be forwarded to the presenting view controller if called on the presentee. If you want to be more explicit about it -- and you'll need to be in some cases, like when one modal view controller is presented from another -- you can explicitly reference the presenter with self.presentingViewController and call dismiss... from there.)
You see the delegate business in some apps because it's one way of notifying view controller A about whatever the user did while in view controller B... but it's not the only way. There's KVO, notifications, or just plain calling A's methods after referencing it with self.presentingViewController (assuming B knows it's always getting presented by A). And if A doesn't need to know about what happened in B (say, because the user hit a Cancel button), there's no need to do any of that -- you can just dismiss the modal and be done with it.
In iOS 6 and later, unwind segues add another option, providing a little bit of "storyboard magic" for dismissing modal view controllers (or otherwise "backing out" of a sequence of segues). But this approach still requires some code -- you can't set it up entirely in storyboard. On the plus side, though, that code provides a path for getting info from the view controller being dismissed (B) to the one that presented it (A).
Apple has a tech note about unwind segues that covers them in detail, but here's the short version:
Define an IBAction method on the view controller class you want to unwind to -- the one that presents a modal view controller, not the modal view controller itself (view controller A in your question). Unlike normal IBAction methods, these should take a parameter of type UIStoryboardSegue *; e.g.
- (IBAction)unwindToMainMenu:(UIStoryboardSegue*)sender
In the presented view controller (B in the question), wire a control to the green Exit icon, and choose the method you defined.
In your unwind method implementation, you can refer to the segue's sourceViewController to retrieve information from the view controller being dismissed. You don't need to call dismissViewControllerAnimated:completion: because the segue handles dismissing the view controller that's going away.
There is storyboard magic to achieve this. It's known as an unwind segue. In A's .h file you implement whatever "target action" style methods you need for however many unwind segues you need. For a modal, it's usually two (cancel and save). So in my A.h file I would add:
// A.h file
- (IBAction)myCancelUnwindSegueCallback:(UIStoryboardSegue *)segue;
- (IBAction)mySaveUnwindSegueCallback:(UIStoryboardSegue *)segue;
Now, in your storyboard, if you have a segue from A to B. You can now do a "target action" style control drag from your cancel/save buttons in B to the green "Exit" icon at the bottom of the B controller in your storyboard. When you do this, Xcode will pick up the two methods we created (since they're in A's header file and they have the correct signature (e.g. IBAction and UIStoryboardSegue *.) and B is the destination of a segue from A) So, there you have it. You have the storyboard magic you were looking for!
In the implementation of the two callbacks, you would have something such as:
// A.m file
- (IBAction)myCancelUnwindSegueCallback:(UIStoryboardSegue *)segue {
UIViewController *modalGoingAway = segue.sourceViewController;
// Do something (like get data) from modalGoingAway if you need to...
}
- (IBAction)mySaveUnwindSegueCallback:(UIStoryboardSegue *)segue {
UIViewController *modalGoingAway = segue.sourceViewController;
// Do something (like get data) from modalGoingAway if you need to...
}
Lastly, if this approach meets your needs, great. You're done. However, I still wire up the whole protocol delegate/dataSource design pattern if "on cancel" or "on save" I want to perform some operations on B's private properties before passing control over to A to remove B from the view hierarchy.

IOS custom button

I need a custom button like Instragram has in profile tab (the buttons that shows the number of photos and followers) but i don't know how to start to implement it.
Do i need to subclass UIButton or is there other way easier?
I think, the easiest approach would be to create a UIButtom with the typeUIButtonTypeCustom and add a subview to it with imageviews and labels as subviews to create the UI. Composition over inheritance.
Subclassing UIButton seems to me to be the obvious solution. I agree that subclassing UIViewController makes no sense. You don't use UIViewController objects to manipulate individual subviews within a view hierarchy controlled by another UIViewController object.
There are plenty of ways to do this, personally I would subclass UIViewController. Then you can edit its .xib in interface builder to make it look however you want and set different values programmatically. Then to detect a tap on the button you can just use the touchesBegan and touchesEnded (I'm pretty sure those aren't complete method names, check in the docs for more info on them) methods. If you want you could also set up a UITapGestureRecognizer for the view instead.

Should I use IB or Subclass UIView

So, I developed a kind of drop down button class.
Let's call it DDButton.
I mainly export one function :
-(void) addButtonWithImage:(UIImage*)image andTarget:(id)target andSelector:(SEL)selector
which lets the user add another button to the drop down.
I will need to use DDButton in different screens of my app.
I would like to use it like:
DDButton* ddb = [[DDButton alloc] initWithFrame:rect];
[ddb addButtonWithImage....]
[ddb addButtonWithImage....]
My question is since I never subclassed UIView before how should I implement it, and how should I use it later ?
Do I use IB and create a stub UIView which I'll connect to the DDButton in the Identity Pane ?
if so , how exactly I instantiate the view later on.
Or,
Do I subclass UIView ? if so , what methods I should override ? Do you I setup my buttons in the initializer ? in LayoutSubView ? In drawRect ?
I would love to hear the best approach here.
Thanks!
Edit
Let's say I choose the IB way : I have a main button which I set regardless of the
addButtonWithImage() calls, actually all calls to addButtonWithImage just "append" to that button. I want to main button to be the size of the view, until other buttons are added and then the view grows appropriately. However, I want the size of the view to be chosen by the user at first...using setFrame I guess.
Meaning in the awakeFromNib I can't count on the frame size yet (it only take the xib size I assume). So where would I setup my main button ? LayoutSubView ? setFrame ? I'm not sure.
Add your view to the interface in IB as a UIView, then change the class in the identity pane. If you need to do initialization in code, use a -(void)awakeFromNib method. I would suggest setting up the buttons when they are added in addButtonWithImage....
I'd probably do a subclass, building views in code is a good thing to learn.
Override drawrect: to do any custom drawing you need to do, if you're just adding a UIImageview or something and doing positioning you could just override initWith...: and do your custom initialisations.