Loading Views ObjC Single View Controller - ios

The way I went about coding my app was to load my views through methods in my ViewController.m file.'s implementation. After loading views my app progressed through menus and displays by button actions and updated my setting the self.view to different displays generated by my methods. The problem with this is my memory consumption. Each time a button is pressed my methods that display the next "view" becomes a child and so on. This chews up memory like crazy and I don't want to recode everything (I didn't use storyboard I used only coded). I tried creating a method in an infinite loop that switched views but it wouldn't update self.view. I need a way of switching views using only code (displaying my views with storyboard isn't possible, too complicated to align).
example code:
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
...global vars...
-(void)viewDidLoad {
[super viewDidLoad];
...setup...
[self Load_Main_Menu];
}
-(void)Load_Main_Menu {
...setup UIView display...
UIButton *Button = [UIButton buttonWithType:UIButtonTypeCustom];
[Button addTarget:self action:#selector(PlayButtonPressed:) forControlEvents:UIControlEventTouchUpInside];
self.view=display;
}
-(void)PlayBt:(UIButton*)sender{
[self load_Other_Menus];
}
All navigation and UI is handled as such.
Any ideas for improving memory management and navigation?

try this
-(void)viewDidLoad {
[super viewDidLoad];
...setup...
[self setupButton];
[self displayView];
}
-(void)setupButton {
UIButton *Button = [UIButton buttonWithType:UIButtonTypeCustom];
[Button addTarget:self action:#selector(displayView) forControlEvents:UIControlEventTouchUpInside];
..add button to view
}
-(void)displayView {
...setup UIView display...
self.view=display;
}

Related

UIButton not working on first touch and working on second touch ios

I am using SWRevealViewController to show right side menu. I am not using BarButtons but i have taken a normal button as my app dont have Navigation bar. The problem i am facing is when i touch that button the methods work properly but i cant see the menu bar coming in, and on second touch the side menu comes in. I am not able to figure out the issue. Help Much Appreciated.
'
-(IBAction)btnMenuClicked:(id)sender
{
SideMenuTableViewController *sidemenuController = (SideMenuTableViewController *)revealViewController.rightViewController;
sidemenuController.pitcherArray = [nTimerArray mutableCopy];
revealViewController.rearViewRevealWidth = self.view.bounds.size.width - 50;
if (revealViewController)
{
[self.btnMenu addTarget:self.revealViewController action:#selector(rightRevealToggle:) forControlEvents:UIControlEventTouchUpInside];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
[self.view addGestureRecognizer:self.revealViewController.tapGestureRecognizer];
}
[sidemenuController.tableView reloadData];
}
'
Import SWRevealViewController first
#import "SWRevealViewController.h"
Create a property for the button as follows...
#property (weak, nonatomic) IBOutlet UIButton *rearButton;
And in the ViewDidLoad method just use this code. It will help you with your problem.
- (void)viewDidLoad {
[super viewDidLoad];
SWRevealViewController *revealViewController = self.revealViewController;
if ( revealViewController )
{
[self.rearButton addTarget:self.revealViewController action:#selector(revealToggle:) forControlEvents:UIControlEventTouchUpInside];
[self.view addGestureRecognizer:self.revealViewController.panGestureRecognizer];
}
}
remove tapGestureRecognizer. It might be work
[self.view addGestureRecognizer:self.revealViewController.tapGestureRecognizer];
For me works if I remove the line of panGestureRecognizer

UISegmentedControl not firing UIControlEventValueChanged

I'm working on the IOS Programming: Big Nerd Ranch Guide Chapter 5 Silver Challenge and am wracking my brain trying to hook up the UISegmentedControl's change event.
Controller.m's initializer contains:
[segmentedControl addTarget:self
action:#selector(onChangeSegment:)
forControlEvents:UIControlEventValueChanged];
Controller.m also contains:
- (void) onChangeSegment:(id)sender
{
NSLog(#"Phew made it!");
}
Controller.h contains:
#interface
//...
- (void) onChangeSegment:(id)sender;
#end
All appears to be in place, however when the active segment is changed from the UI, onChangeSegment is never messaged. This is the case with either the simulator or my iPhone4S (IOS7 and XCode 5).
Any help or direction is appreciated. Please let me know if I can supply more details.
Please do a
NSLog(#"segmentedControl: %#", segmentedControl);
before your target action adding. Its possible you're trying to addTarget: to a nil object. If you're doing this via Interface Builder, the addTarget should be done in the -viewDidLoad: method.
What I did in a sample app was:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.segmentedControl addTarget:self action:#selector(change:) forControlEvents:UIControlEventValueChanged];
}
-(void)change:(UISegmentedControl *)control{
NSLog(#"changed!");
}
The Segmented Control was created using IB and linked to the view controller's header.
You said:
Controller.m's initializer contains:
In initializer you don't have IBOutlets from XIB so segmentedControl == nil at that time and method call does nothing. Views are usually configured in -viewDidLoad method where IBOutlets are guaranteed to be loaded from xib. So simply move
[segmentedControl addTarget:self
action:#selector(onChangeSegment:)
forControlEvents:UIControlEventValueChanged];
to -viewDidLoad method. Other things seem to be ok.

Issues with UINavigationItem changing properties at runtime

i have a Navigation Bar, wich contains a Navigation Item, which contains 2 Bar Buttons, these are created in the Storyboard, and i wanted to change 1 of the buttons at runtime, now this works:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UINavigationItem *thisNavBar = [self myNavigationItem];
thisNavBar.rightBarButtonItem = nil; // this works, it gets removed
UIBarButtonItem *insertBtn = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemEdit target:self action:#selector(insertSkemaItem:)];
thisNavBar.rightBarButtonItem = insertBtn; // this also works, it sets the btn
}
Now, in my other method, which is called by another controller, it does not work
- (void)callChildChange {
...
// remove old btn
UINavigationItem *thisNavBar = [self skemaNavigationItem];
thisNavBar.rightBarButtonItem = nil; // does not work?
}
There is nothing wrong with the method, it runs just fine, but the nav btn item does not get removed ?
skemaNavigationItem is a Navigation item, declared in the .h file which links the navigation item i made via the storyboard.
Your UI items need to be added to your code (by ctrl-dragging) in the header file (.h) so they can be publicly accessed from other classes/view controllers.
Presuming you've done this, hiding a UI item is best done by using
relevantClass.yourViewObject.hidden = YES;
or if you really need to delete it for good,
[relevantClass.yourViewObject.view removeFromSuperView];
Edits
Options for changing target method:
Declare #property (nonatomic, assign) BOOL myButtonWasPressed; and:
- (IBAction) myButtonPressed
{
if (!self.myButtonWasPressed)
{
// This code will run the first time the button is pressed
self.myButton.text = #"New Button Text";
self.myButtonWasPressed = YES;
}
else
{
// This code will run after the first time your button is pressed
// You can even set your BOOL property back, and make it toggleable
}
}
or
- (IBAction) myButtonWasPressedFirstTime
{
// do what you need to when button is pressed then...
self.myButton.text = #"New Button Text";
[self.myButton removeTarget:self action:#selector(myButtonPressedFirstTime) forControlEvents:UIControlEventTouchUpInside];
[self.myButton addTarget:self action:#selector(myButtonPressedAgain) forControlEvents: UIControlEventTouchUpInside];
}
- (IBAction) myButtonWasPressedAgain
{
// this code will run the subsequent times your button is pressed
}

iPad undo button (a-la Keynote and other apps)

In Keynote (and other apps), I've noticed the "standard" interface of doing Undo/Redo is by providing an Undo button on the tool bar.
Clicking the button (that is always enabled) Undos the recent operation.
(If there is not recent operation to undo, it will show the Undo/Redo menu).
Long-clicking the Undo button opens an Undo/Redo menu.
I searched for methods of implementing this, and the best answer I found so far is at the following link.
I wonder if anyone knows of a simpler way?
Thanks!
After reviewing all methods and discussing with friends, below is the solution I used, for a UIBarButtonItem the responds to both taps and long-press (TapOrLongPressBarButtonItem).
It is based on the following principals:
Subclass UIBarButtonItem
Use a custom view (so it's really trivial to handle the long-press - since our custom view has no problem responding to a long-press gesture handler...)
... So far - this approach was in the other SO thread - and I didn't like this approach since I couldn't find and easy enough way of making the custom view appear like an iPad navigation bar button... Soooo...
Use UIGlossyButton by Water Lou (thanks water!). This use is encapsulated within the subclass...
The resulting code is as follows:
#protocol TapOrPressButtonDelegate;
#interface TapOrPressBarButtonItem : UIBarButtonItem {
UIGlossyButton* _tapOrPressButton;
__weak id<TapOrPressButtonDelegate> _delegate;
}
- (id)initWithTitle:(NSString*)title andDelegate:(id<TapOrPressButtonDelegate>)delegate;
#end
#protocol TapOrPressButtonDelegate<NSObject>
- (void)buttonTapped:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem;
- (void)buttonLongPressed:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem;
#end
#implementation TapOrPressBarButtonItem
- (void)buttonLongPressed:(UILongPressGestureRecognizer*)gesture {
if (gesture.state != UIGestureRecognizerStateBegan)
return;
if([_delegate respondsToSelector:#selector(buttonLongPressed:withBarButtonItem:)]) {
[_delegate buttonLongPressed:_tapOrPressButton withBarButtonItem:self];
}
}
- (void)buttonTapped:(id)sender {
if (sender != _tapOrPressButton) {
return;
}
if([_delegate respondsToSelector:#selector(buttonTapped:withBarButtonItem:)]) {
[_delegate buttonTapped:_tapOrPressButton withBarButtonItem:self];
}
}
- (id)initWithTitle:(NSString*)title andDelegate:(id<TapOrPressButtonDelegate>)delegate {
if (self = [super init]) {
// Store delegate reference
_delegate = delegate;
// Create the customm button that will have the iPad-nav-bar-default appearance
_tapOrPressButton = [UIGlossyButton buttonWithType:UIButtonTypeCustom];
[_tapOrPressButton setTitle:title forState:UIControlStateNormal];
[_tapOrPressButton setNavigationButtonWithColor:[UIColor colorWithRed:123.0/255 green:130.0/255 blue:139.0/255 alpha:1.0]];
// Calculate width...
CGSize labelSize = CGSizeMake(1000, 30);
labelSize = [title sizeWithFont:_tapOrPressButton.titleLabel.font constrainedToSize:labelSize lineBreakMode:UILineBreakModeMiddleTruncation];
_tapOrPressButton.frame = CGRectMake(0, 0, labelSize.width+20, 30);
// Add a handler for a tap
[_tapOrPressButton addTarget:self action:#selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
// Add a handler for a long-press
UILongPressGestureRecognizer* buttonLongPress_ = [[UILongPressGestureRecognizer alloc] initWithTarget:self
action:#selector(buttonLongPressed:)];
[_tapOrPressButton addGestureRecognizer:buttonLongPress_];
// Set this button as the custom view of the bar item...
self.customView = _tapOrPressButton;
}
return self;
}
// Safe guards...
- (id)initWithImage:(UIImage *)image style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action {
NSLog(#"%s not supported!", __FUNCTION__);
return nil;
}
- (id)initWithImage:(UIImage *)image landscapeImagePhone:(UIImage *)landscapeImagePhone style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action {
NSLog(#"%s not supported!", __FUNCTION__);
return nil;
}
- (id)initWithTitle:(NSString *)title style:(UIBarButtonItemStyle)style target:(id)target action:(SEL)action {
NSLog(#"%s not supported!", __FUNCTION__);
return nil;
}
- (id)initWithBarButtonSystemItem:(UIBarButtonSystemItem)systemItem target:(id)target action:(SEL)action {
NSLog(#"%s not supported!", __FUNCTION__);
return nil;
}
- (id)initWithCustomView:(UIView *)customView {
NSLog(#"%s not supported!", __FUNCTION__);
return nil;
}
#end
And all you need to do is:
1. Instantiate is as follows:
TapOrPressBarButtonItem* undoMenuButton = [[TapOrPressBarButtonItem alloc] initWithTitle:NSLocalizedString(#"Undo", #"Undo Menu Title") andDelegate:self];
2. Connect the button to the navigation bar:
[self.navigationItem setLeftBarButtonItem:undoMenuButton animated:NO];
3. Implement the TapOrPressButtonDelegate protocol, and you're done...
-(void)buttonTapped:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem {
[self menuItemUndo:barButtonItem];
}
-(void)buttonLongPressed:(UIButton*)button withBarButtonItem:(UIBarButtonItem*)barButtonItem {
[self undoMenuClicked:barButtonItem];
}
Hope this helps anyone else...
If you are using IB (or in Xcode4 the designer...i guess it is called) then you can select "Undo" from the First responder and drag that action to a button. I can give you more specific instructions if that doesn't cover it.
Here's what it looks like
It's on the left underneath the column "Received actions" at the bottom
I believe the key is actually in the UINavigationBar itself. Unlike UIButtons or other normal touch tracking objects, I suspect UIBarItems don't handle their own touches. They don't inherit UIResponder or UIControl methods. However UINavigationBar of course does. And I've personally added gestures straight to a UINavigationBar many times.
I suggest you override touch handling in a UINavigationBar subclass and check the touches against its children. If the child is your special Undo button you can handle it accordingly.
UIButton* undoButton = [UIButton buttonWithType:UIButtonTypeCustom];
[undoButton addTarget:self action:#selector(undoPressStart:) forControlEvents:UIControlEventTouchDown];
[undoButton addTarget:self action:#selector(undoPressFinish:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem* navButton = [[[UIBarButtonItem alloc] initWithCustomView:undoButton] autorelease];
self.navigationItem.rightBarButtonItem = navButton;
You don't necessarily have to add the UIBarButtonItem as the rightBarButtonItem, this is just and easy way to show you how to create your UIBarButtonItem with a custom view that is the UIButton you want to handle events.
You'll need to implement the undoPressStart: and undoPressFinish: by maintaining state. I'd say on start, store the current NSDate or some granular representation of the time. On finish, if check the time elapsed and if it is beyond a certain threshold, show the menu - otherwise (as well as if the start date was never captured) perform the undo.
As an improvement, you'll likely want to observe the UIControlEventTouchDragExit event as well to cancel the long press.

UIView and UIViewController

I know this is really basic stuff but i need to understand whether my understanding of this is correct.
So what i want to do is this. I want an view with a label on which when double tapped flips and loads another view. On the second view i want a UIPickerView and above i have a button saying back. Both views will be of same size as an UIPickerView which is 320px x 216px.
What i am thinking of to do is create two UIViewclasses named labelView and pickerView. I would then create a viewController which on loadView loads labelView then when user double taps the labelView i get an event in labelView class which is sent to my viewController that then can unload loadView and load the pickerView.
Does this sound as the best way to do this ? Is there a simpler way ? I am also unsure how i route the event from the labelView class to the viewControllerclass.
I dont exactly know the most efficient way to do it(as i am also now to this language),but it is for sure that i have solved ur problem. I made a simple program for that.Three classes involved here in my eg are BaseViewController (which will show two views),LabelView and PickerView (according to ur requirement).
In LabelView.h
#protocol LabelViewDelegate
-(void)didTapTwiceLabelView;
#end
#interface LabelView : UIView {
id <LabelViewDelegate> delegate;
}
#property(nonatomic,retain)id <LabelViewDelegate> delegate;
-(void)didTouch;
#end
In LabelView.m
#synthesize delegate;
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self)
{
UILabel* labl = [[UILabel alloc] initWithFrame:CGRectMake(10, 5, frame.size.width-20,20)];
labl.text = #"Some Text";
[self addSubview:labl];
[labl release]; labl = nil;
self.backgroundColor = [UIColor grayColor];
UITapGestureRecognizer* ges = [[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTouch)] autorelease];
ges.numberOfTapsRequired = 2;
[self addGestureRecognizer:ges];
}
return self;
}
-(void)didTouch
{
[delegate didTapTwiceLabelView];
}
//=============================================================
In Pickerview.h
#protocol PickerViewDelegate
-(void)didTapBackButton;
#end
#interface PickerView : UIView <UIPickerViewDelegate,UIPickerViewDataSource>{
id <PickerViewDelegate> delegate;
}
#property(nonatomic,retain)id <PickerViewDelegate> delegate;
#end
In Pickerview.m
#implementation PickerView
#synthesize delegate;
-(id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self)
{
UIPickerView* picker = [[UIPickerView alloc] initWithFrame:CGRectMake(0, 30, 320, 216)];
picker.delegate = self;
picker.dataSource = self;
[self addSubview:picker];
[picker release]; picker = nil;
self.frame = CGRectMake(frame.origin.x, frame.origin.y, 320, 250);
UIButton* btn = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[btn setFrame:CGRectMake(10, 1, 50, 27)];
[btn setTitle:#"Back" forState:UIControlStateNormal];
[btn addTarget:self action:#selector(backButton) forControlEvents:UIControlEventTouchUpInside];
[self addSubview:btn];
}
return self;
}
-(void)backButton
{
[delegate didTapBackButton];
}
//====================================================================
in BaseViewController.h
#import "LabelView.h"
#import "PickerView.h"
#interface VarticalLabel : UIViewController<UITextFieldDelegate,PickerViewDelegate,LabelViewDelegate> {
PickerView* myPickerView;
LabelView* myLabelView;
}
#end
In BaseViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
myPickerView= [[PickerView alloc] initWithFrame:CGRectMake(0, 50, 320, 250)];
[self.view addSubview:myPickerView];
myPickerView.delegate = self;
myLabelView= [[LabelView alloc] initWithFrame:CGRectMake(0, 50, 320, 250)];
[self.view addSubview:myLabelView];
myLabelView.delegate = self;
myPickerView.hidden = YES;
}
#pragma mark PickerViewDelgate
-(void)didTapBackButton
{
myPickerView.hidden = YES;
myLabelView.hidden = NO;
}
#pragma mark LabelViewDelegate
-(void)didTapTwiceLabelView
{
myPickerView.hidden = NO;
myLabelView.hidden = YES;
}
To get events from a button to the view controller, just hook up the button's event, e.g. touch up inside, to a method in the view controller, using interface builder. (Double tapping is probably more complicated though.)
When you say 'flips', do you mean it actually shows an animation of flipping over a view to show a 'reverse' side? Like in the weather app when you hit the 'i' button? I'm assuming this is what you mean.
Perhaps check TheElements sample example on the iPhone Reference Library, it has an example of flip animation.
Btw, it's not strictly necessary to unload the loadView that is being 'hidden' when you flip -- it saves you having to construct it again when you flip back -- but it may be pertinent if you have memory use concerns, and/or the system warns you about memory being low.
Also, what do you mean by "create a UIView"? Do you mean subclass UIView, or just instantiate a UIVIew and add children view objects to it? The latter is the usual strategy. Don't subclass UIView just because you want to add some things to a UIView.
If you've got one screen of information that gives way to another screen of information, you'd normally make them separate view controllers. So in your case you'd have one view controller with the label and upon receiving the input you want, you'd switch to the view controller composed of the UIPickerView and the button.
Supposing you use Interface Builder, you would probably have a top level XIB (which the normal project templates will have provided) that defines the app delegate and contains a reference to the initial view controller in a separate XIB (also supplied). In the separate XIB you'd probably want to add another view controller by reference (so, put it in, give it the class name but indicate that its description is contained in another file) and in that view controller put in the picker view and the button.
The point of loadView, as separate from the normal class init, is to facilitate naming and linking to an instance in one XIB while having the layout defined in another. View controllers are alloced and inited when something that has a reference to them is alloced and inited. But the view is only loaded when it is going to be presented, and may be unloaded and reloaded while the app is running (though not while it is showing). Generally speaking, views will be loaded when needed and unnecessary views will be unloaded upon a low memory warning. That's all automatic, even if you don't put anything in the XIBs and just create a view programmatically within loadView or as a result of viewDidLoad.
I've made that all sound more complicated than your solution, but it's actually simpler because of the amount you can do in Interface Builder, once you're past the curve of learning it. It may actually be worth jumping straight to the Xcode 4 beta, as it shakes things up quite a lot in this area and sites have reported that a gold master was seeded at one point, so is likely to become the official thing very soon.
With respect to catching the double tap, the easiest thing is a UITapGestureRecognizer (see here). You'd do something like:
// create a tap gesture recogniser, tell it to send events to this instance
// of this class, and to send them via the 'handleGesture:' message, which
// we'll implement below...
UITapGestureRecognizer *tapGestureRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleGesture:)];
// we want double taps
tapGestureRecognizer.numberOfTapsRequired = 2;
// attach the gesture recogniser to the view we want to catch taps on
[labelView addGestureRecognizer:tapGestureRecognizer];
// we have an owning reference to the recogniser but have now given it to
// the label. We don't intend to talk to it again without being prompted,
// so should relinquish ownership
[tapGestureRecognizer release];
/* ... elsewhere ... */
// the method we've nominated to receive gesture events
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer
{
// could check 'gestureRecognizer' against tapGestureRecognizer above if
// we set the same message for multiple recognisers
// just make sure we're getting this because the gesture occurred
if(gestureRecognizer.state == UIGestureRecognizerStateRecognized)
{
// do something to present the other view
}
}
Gesture recognisers are available as of iOS 3.2 (which was for iPad only; so iOS 4.0 on iPhone and iPod Touch).

Resources