i am in initial phase of development and i want to make sure i am in right direction with regards to memory management.
i have a view controller named LayoutViewController with xib.I have a custom ui-subclass with its xib named LayoutContainerView which basically contains a scrollview. I am using LayoutContainerView in LayoutViewController xib by IBOutlet.
I have an another UIView subclassed, which contains a view with background image, some labels and a transparent button with same frame as of viw.I am adding this custom controll in LayoutContainerView's scrollview.
my view controller .h looks like this:
#import <UIKit/UIKit.h>
#protocol LayoutVcRemovedProtocol;
extern const char* MyConstantKey;
#interface LayoutViewController : UIViewController
// public properties
#property (nonatomic, copy) NSString *databasePath;
#property (nonatomic, weak) id<LayoutVcRemovedProtocol> layoutVcRemovedProtocolDelegate;
#end
#protocol LayoutVcRemovedProtocol<NSObject>
-(void) layoutVcRemovedProtocolMethod;
#end
=========================
**some of relevant code of **implementation** file looks like this:**
//private stuffs goes here
const char* MyConstantKey = "MyConstantKey";
#interface LayoutViewController () <UIActionSheetDelegate, UIAlertViewDelegate>
#property(nonatomic, weak) IBOutlet LayoutContainerView *layoutContainerView;
#property(nonatomic, weak) IBOutlet UIButton *backbutton;
#property(nonatomic, weak) IBOutlet UILabel *layoutNameLabel;
#property (nonatomic, strong) UIView *baseView;
#property (nonatomic, strong) NSMutableArray *layoutModelArray;
-(IBAction)backButtonPressed;
#end
#implementation LayoutViewController
//my viewDidLoad and viewWillAppear look like this:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
[self adjustLayoutContainerFrameAndSetDataBasePath];
}
-(void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.layoutNameLabel.text = [Utils getLayoutName];
[self getLatestData];
}
-(void) getLatestData
{
[self setUpDataSource];
[self setUpComponentsOnLayoutScreen];
}
#pragma mark - datasource method
-(void)setUpDataSource
{`
self.layoutModelArray = (NSMutableArray *)[LAYOUTMODULE getAllLayoutData];
}`
-(void)setUpComponentsOnLayoutScreen
{`
for (int i = 0; i < self.layoutModelArray.count; i++)
{
Layout *layout = [self.layoutModelArray objectAtIndex:i];
[self drawViewWithLayoutObject:layout];
}
[self.layoutContainerView.scrollView adjustContentSize];
}
this is what i am trying to manage memory:
-(void) cleanLayoutModelArray
{
if (self.layoutModelArray != nil && self.layoutModelArray.count >0)
{
[self.layoutModelArray removeAllObjects];
}
}
-(void) cleanComponents
{
[self.layoutContainerView.scrollView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
}
//user events
-(void) placeOrderForLayout:(Layout *)layout
{
[DELEGATE showLandscapeLoading];
//web service COMMUNICATION HERE here
OrderModule *oModule = [OrderModule sharedModule];
NSDictionary *requestDictionary = [NSDictionary dictionaryWithObjectsAndKeys:oModule.currentOrder.mOrderId,#"order_id",oModule.currentOrder.mPosId,#"pos_id",[Utils currentDate],#"book_date", layout.componentId, #"component_id", nil];
BOOL status = [LAYOUTMODULE placeComponentOrderThroughAPI:requestDictionary];
if (status == TRUE)
{
[self performCleanUp];
[self getLatestData];
}
[DELEGATE stopLandscapeLoading];
}
help me or any suggestion for:
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
what ever i try in memoryWarningDelegate view controller becomes black screen.
You've got a lot of #properties marked 'weak' in your code - when you mark things as weak, that means that something else is keeping track of whether it should still exist. Here you're doing it with IBOutlet items, which your controller should be keeping track, so they should be marked 'strong,' not weak. I'd review all your usage of 'weak' in this. Also refer to Apple's excellent doc on memory management. ARC will be handling most of your memory management for you. Usually, the only thing you need to do in didReceiveMemoryWarning, is to set to nil anything that's a large object, say a video or webpage you can reload if the user needs it again. Often, there's not much that you can free up at this time in your typical view. Note also that iOS devices have a fairly substantial memory footprint these days, so you should not worry about small data structures remaining resident in memory as long as they have the potential to be needed again. If you're having out of memory issues, I'd run with instruments and check for leaks.
Related
I'm trying to use an xib file (which is just a simple view) on my viewcontroller more than once. I can add it on my viewcontroller more than once and interact with both of them. The question is, how can i distinguish between these views to know which one i'm clicking?
For example, when i tap on my firstview, i want to print "apples" and when i tap on second view i wan to print "oranges"
Below you can see my code and here is github repo for you to play with my code: https://github.com/TimurAykutYildirim/demoView/tree/multiple-instance
ViewController.h
#import <UIKit/UIKit.h>
#import "Mini.h"
#interface ViewController : UIViewController <SelectionProtocol>
#property (weak, nonatomic) IBOutlet Mini *miniView;
#property (weak, nonatomic) IBOutlet Mini *miniView2;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.miniView.delegate = self;
self.miniView2.delegate = self;
}
-(void) isClicked {
NSLog(#"apples");
NSString * storyboardName = #"Main";
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:storyboardName bundle: nil];
UIViewController * vc = [storyboard instantiateViewControllerWithIdentifier:#"SecondViewController"];
[self presentViewController:vc animated:YES completion:nil];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Mini.h
#import <UIKit/UIKit.h>
#protocol SelectionProtocol;
#interface Mini : UIView
#property (nonatomic, weak) id<SelectionProtocol> delegate;
- (IBAction)btnClick:(id)sender;
#end
#protocol SelectionProtocol <NSObject>
#required
-(void) isClicked;
#end
Mini.m
#import "Mini.h"
#implementation Mini
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self load];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self load];
}
return self;
}
- (void)load {
UIView *view = [[[NSBundle bundleForClass:[self class]] loadNibNamed:#"Mini" owner:self options:nil] firstObject];
[self addSubview:view];
view.frame = self.bounds;
}
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClicked];
}
}
#end
There are lots of ways that you could do that. Here are two:
Add a sender parameter to your -[ViewController isClicked] method, and change Mini so that calls to -isClicked pass in a pointer to self. Then the code in -isClicked can compare that to each of the instances of Mini that it knows about, i.e. self.miniView and self.miniView2, to see if either of those is the one that sent the message.
Add a property to Mini that lets you distinguish between the two, e.g. name. You can configure that property in -viewDidLoad, like self.miniView.name = #"apples", or you can even do it in the .xib file using "user defined runtime attributes." Then you can have Mini pass it's name property as a parameter to methods that need to know which instance of Mini is the caller. (Or, combine 1 & 2 and pass a reference to self so that ViewController can examine the name parameter or anything else it wants.)
I'm assuming from your question that somewhere in the Mini xib there is an outlet with the text "apples" (or whichever fruit for that particular xib).
In that case, you can just change your protocol to:
- (void)isClickedFromView:(Mini *)mini
In the delegate (ViewController.m) change the btnClick action to:
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClickedFromView:self];
}
}
Add an outlet like fruitLabel to your Mini class.
#property (weak, nonatomic) IBOutlet UILabel *fruitLabel
Now when the delegate gets the call you can call:
NSLog(#"Fruit: %#", mini.fruitLabel.text);
===== Additional answer for if the data (in this case fruits) is available in code =====
If you have the data programmatically already (like an array of fruits), It might be easier to just put the Mini classes in an array ordered the same way.
So if miniViewArray contains an array of your Mini classes,
and fruitArray contains an array of NSStrings of fruits you can do:
At the time you set the miniView's delegate you can add them to the array..something like:
NSArray *fruitArray = #[ #"apples", #"oranges" ];
NSArray *miniViewArray = #[ miniView, miniView2 ];
Then in the delegate call you can do (Using the same protocol change as above):
- (void)isClickedFromView:(Mini *)mini {
NSInteger fruitIndex = [miniViewArray indexOfObject:mini];
NSString fruitName = fruitArray[fruitIndex];
NSLog(#"Fruit: %#", fruitName);
}
I use ENUM for distinguish between the view type.
typedef NS_ENUM(NSUInteger, <#MyViewType#>) {
<#MyViewTypeDefault#>,
<#MyViewTypeA#>,
<#MyViewTypeB#>,
};
#property (nonatomic, assign) MyEnum viewType;
Protocol:
-(void) isClickedForViewType:(MyEnum)viewType;
Common approach for calling delegate method is to include caller as a param. In your case it should be something like this:
- (IBAction)btnClick:(id)sender {
if ([self.delegate conformsToProtocol:#protocol(SelectionProtocol)]) {
[self.delegate isClickedOnMiniView:self];
}
}
Basically, this convention was created to cover such cases - one object is a delegate of many similar ones.
Check apple docs for examples of this convention:
https://developer.apple.com/documentation/uikit/uitableviewdelegate
This question already has answers here:
Passing data between view controllers
(45 answers)
Access Variable from Another Class - Objective-C
(2 answers)
Closed 8 years ago.
I'm creating a project so that I have two view controllers, connected by a modal segue with an identifier "login_success".
In the primary view controller, I have a text field that takes the input of whatever the user types, and a button to perform the segue.
In the next controller, I have a label that is supposed to print out whatever the user typed.
My code:
DICViewController.h (First View Controller):
#import <UIKit/UIKit.h>
#interface DICViewController : UIViewController <UITextFieldDelegate>
#property (weak, nonatomic) IBOutlet UITextField *txtUsername;
- (IBAction)sigininClicked:(id)sender;
- (IBAction)backgroundTap:(id)sender;
#end
DICViewController.m:
#import "NewViewController.h"
#interface DICViewController ()
#end
#implementation DICViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)sigininClicked:(id)sender {
{
[self performSegueWithIdentifier:#"login_success" sender:self];
}
}
- (IBAction)backgroundTap:(id)sender {
[self.view endEditing:YES];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
return YES;
}
#end
NewsViewController.h (The other view controller):
#import <UIKit/UIKit.h>
#interface NewViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *steamId; //my label
#end
NewsViewController.m:
No code was added here.
Thanks in advance to anyone that can help.
Again, I would like to be able to set the text in the label equal to the text the user types in the text field.
When performing a segue the preferred way to pass data from one view controller to another is to make use of the method -prepareForSegue:sender:.
In your case the following lines of code should work:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
NewsViewController *newsVC = segue.destinationViewController;
[newsVC view]; // this loads the view so that its subviews (the label) are not nil
newsVC.steamID.text = self.txtUsername.text;
}
(Place this method anywhere in your DICViewController.m.)
I think The better way is to set global variables. Just make normal class
variables.h
#import <Foundation/Foundation.h>
#interface variables : NSObject {
float VariableYouWant;
}
+ (_tuVariables *)sharedInstance;
#property (nonatomic, assign, readwrite) float VariableYouWant;
and
variables.m
#import "variables.h"
#implementation variables
#synthesize VariableYouWant = _VariableYouWant;
+ (_tuVariables *)sharedInstance {
static dispatch_once_t onceToken;
static variables *instance = nil;
dispatch_once(&onceToken, ^{
instance = [[variables alloc] init];
});
return instance;
}
- (id)init {
self = [super init];
if (self) {
}
return self;
}
#end
Way to use:
import header file of variables and
variables *globals = [variables sharedInstance];
and simply access variables with
globals.VariableYouWant =
I'm trying to move a UIView across the screen.
UIGravityBehavior and UIPushBehavior both take things like density, velocity and friction into account. I tried to implement my own dynamic behavior that ignores those physics.
My UIDynamicBehavior subclass and some basic implementation
// MYDynamicBehavior.h
#interface MYDynamicBehavior : UIDynamicBehavior
- (void)addItem:(id<UIDynamicItem>)item;
- (void)removeItem:(id<UIDynamicItem>)item;
#end
// MYDynamicBehavior.m
#interface MYDynamicBehavior ()
#property (strong, nonatomic) NSMutableArray *items;
#end
#implementation MYDynamicBehavior
- (void (^)(void))action
{
__weak MYDynamicBehavior *weakSelf = self;
for (UIView *item in weakSelf.items)
item.center = CGPointMake(item.center.x + 10.0, item.center.y);
}
- (instancetype)init
{
if (self=[super init]) {
__weak MYDynamicBehavior *weakSelf = self;
weakSelf.action = ^{
for (UIView *item in weakSelf.items)
item.center = CGPointMake(item.center.x + 10.0, item.center.y);
};
}
return self;
}
- (void)addItem:(id<UIDynamicItem>)item
{
[self.items addObject:item];
}
#end
// ViewController.m
// #includes
#interface ViewController ()
#property (strong, nonatomic) UIDynamicAnimator *ani;
#property (strong, nonatomic) UIView *kin;
#property (strong, nonatomic) MYDynamicBehavior *skywalk;
#end
#implementation ViewController
…
- (void)viewDidLoad
{
[super viewDidLoad];
self.ani = [[UIDynamicAnimator alloc] init];
self.kin = // some view
self.skywalk = [[MYDynamicBehavior alloc] init];
[self.ani addBehavior:self.skywalk];
[self.skywalk addItem:kin];
}
#end
I'm trying to recreate this from memory, I think the basics are here
Anyway, it's my impression from the documentation that the action property is where I need to implement my animation. It doesn't appear that my action black is ever called, however.
This is the closest I've come to a solution, but I still haven't solved this problem yet.
What am I missing? Why isn't my custom UIDynamicBehavior subclass working?
I haven't found the documentation that states this explicitly, and I can't guess at the underlying reason, but I have found that if my custom UIDynamicBehavior classes call their action blocks only if there is a child behavior added which has at least one item in it.
It's weird enough that I think I'm experiencing a side effect rather than this working as intended, but that does reliably get the action block to fire. Would be really interested if anybody could shed light on the why, though. :1
I am developing application for iOS7 in xcode 5. I have a view controller ADMSViewController
The .h file is as follows
#import <UIKit/UIKit.h>
#import "IMTWebView.h"
#interface ADMSViewController : UIViewController <UIWebViewDelegate,IMTWebViewProgressDelegate>
{
NSString *barcode;
NSString *vinNumber;
NSTimer *timer;
}
#property (nonatomic,retain) IBOutlet UIWebView *webView;
#property (nonatomic,retain) IBOutlet UIProgressView *progressView;
#end
The .m file is as follows
#import "ADMSViewController.h"
#import <QuartzCore/QuartzCore.h>
#implementation ADMSViewController
#synthesize webView;
#synthesize progressView;
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://google.com/"]]];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.navigationController setNavigationBarHidden:YES animated:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[self.navigationController setNavigationBarHidden:NO animated:animated];
}
- (void)webView:(IMTWebView *)_webView didReceiveResourceNumber:(int)resourceNumber totalResources:(int)totalResources {
[self.progressView setProgress:((float)resourceNumber) / ((float)totalResources)];
if (resourceNumber == totalResources) {
_webView.resourceCount = 0;
_webView.resourceCompletedCount = 0;
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
self.webView = nil;
self.progressView = nil;
}
#end
The IMTWebview.h file is as follows
#import <UIKit/UIKit.h>
#class IMTWebView;
#protocol IMTWebViewProgressDelegate <NSObject>
#required
- (void) webView:(IMTWebView*)webView didReceiveResourceNumber:(int)resourceNumber totalResources:(int)totalResources;
#end
#interface IMTWebView : UIWebView {
}
#property (nonatomic, assign) int resourceCount;
#property (nonatomic, assign) int resourceCompletedCount;
#property (nonatomic, assign) id<IMTWebViewProgressDelegate> progressDelegate;
#end
the IMTWebCiew.m file is as follows
#import "IMTWebView.h"
#interface UIWebView ()
-(id)webView:(id)view identifierForInitialRequest:(id)initialRequest fromDataSource:(id)dataSource;
-(void)webView:(id)view resource:(id)resource didFinishLoadingFromDataSource:(id)dataSource;
-(void)webView:(id)view resource:(id)resource didFailLoadingWithError:(id)error fromDataSource:(id)dataSource;
#end
#implementation IMTWebView
#synthesize progressDelegate;
#synthesize resourceCount;
#synthesize resourceCompletedCount;
-(id)webView:(id)view identifierForInitialRequest:(id)initialRequest fromDataSource:(id)dataSource
{
[super webView:view identifierForInitialRequest:initialRequest fromDataSource:dataSource];
return [NSNumber numberWithInt:resourceCount++];
}
- (void)webView:(id)view resource:(id)resource didFailLoadingWithError:(id)error fromDataSource:(id)dataSource {
[super webView:view resource:resource didFailLoadingWithError:error fromDataSource:dataSource];
resourceCompletedCount++;
if ([self.progressDelegate respondsToSelector:#selector(webView:didReceiveResourceNumber:totalResources:)]) {
[self.progressDelegate webView:self didReceiveResourceNumber:resourceCompletedCount totalResources:resourceCount];
}
}
-(void)webView:(id)view resource:(id)resource didFinishLoadingFromDataSource:(id)dataSource
{
[super webView:view resource:resource didFinishLoadingFromDataSource:dataSource];
resourceCompletedCount++;
if ([self.progressDelegate respondsToSelector:#selector(webView:didReceiveResourceNumber:totalResources:)]) {
[self.progressDelegate webView:self didReceiveResourceNumber:resourceCompletedCount totalResources:resourceCount];
}
}
#end
My issue is that the progress bar is not animating in the webview. Please help me as I am a newbie to ios development. If there is any other method to implement the same, do inform me.
Thank you in advance for your answers.
For this kind of thing, you can only really 'detect' page size by using Content-Length headers, but otherwise you can still present a loading control (like one of the moving zebra crossing type bars which doesn't show a discrete % progress but shows that activity is occurring.. or even just a UIActivityIndicator). You could try a ready-made class like http://allseeing-i.com/ASIHTTPRequest/ASIWebPageRequest too, which CAN show progress. The author states:
ASIWebPageRequests do not currently support progress tracking for an entire request with its external resources. However, progress updates from external resource requests are passed on to your delegate, so with a bit of work it may be possible to implement this yourself.
I am quite new to iOS development and thus new to the concept of storyboard as well.
As this seems to be the 'new thing', everyone should use, I thought I might give it a try as well.
I got a project here, created with a Foo.xib file.
The xib file has several view objects included.
Then I have a class Foo.h and Foo.m class with following content:
Foo.h
#import <UIKit/UIKit.h>
#interface Foo : UIView
#property (strong, nonatomic) IBOutlet UIView *view01;
#property (strong, nonatomic) IBOutlet UIView *view02;
#property (strong, nonatomic) IBOutlet UIView *view03;
#property (strong, nonatomic) IBOutlet UIView *view04;
- (NSUInteger)viewCount;
#end
Foo.m
#import "Foo.h"
#interface Foo()
#property (nonatomic, strong) NSArray *views;
#end
#implementation Foo
- (id)init {
self = [super init];
if (self) {
self.views = [[NSBundle mainBundle] loadNibNamed:#"Foo" owner:self options:nil];
}
return self;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (NSUInteger)viewCount {
return [self.views count];
}
#end
In my ViewController I would then load all the views and make it scrollable, like this:
#import "ViewController.h"
#import "Foo.h"
#interface ViewController ()
#property (nonatomic, strong) UIScrollView *scrollView;
#property (nonatomic, strong) Foo *views;
#end
#implementation ViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
self.views = [[Foo alloc] init];
CGSize fooSize = self.views.view01.bounds.size;
NSUInteger viewCount = [self.views viewCount];
self.scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, fooSize.width, fooSize.height)];
[self.scrollView setContentSize:CGSizeMake(viewCount*fooSize.width, fooSize.height)];
[self.scrollView setBounces:YES];
[self.scrollView setPagingEnabled:YES];
self.scrollView.delegate = self;
NSArray *views = #[ self.views.view01,
self.views.view02,
self.views.view03,
self.views.view04
];
for (int i=0; i<viewCount; i++) {
UIView *curView = views[i];
CGRect frame = curView.frame;
frame.origin.x = i*fooSize.width;
frame.origin.y = 0;
curView.frame = frame;
[self.scrollView addSubview:curView];
}
[self.view addSubview:self.scrollView];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
However, I have no clue, how to realize this with a storyboard. It seems to me that I have to have a NavigationController which is then linked to the Master View Controller. And now I would have to add a new ViewController for each view? Or is there a way to include all views within one ViewController like I did 'the old way'?
There is a massive mis conception that when using a storyboard it limits you to what you can do. A storyboard is simply like an array of .xib files, it holds many screens in the one file so you can see the entire flow of you app in one place. Inside a storyboard you can create a single viewController and assign a custom viewController class to it and then load / modify what ever you like inside the code of this viewController, as you have done above.
However the benefit of using the storyboard is to have multiple viewController objects so you can design all the screens and navigation there were you can see it, aiding you in debugging and design work.
If you already have the app working without a storyboard and you simply want to use it because its new but keep the old style of coding, you are not going to see much of the benefits. I would suggest following this example from the developer library on how to use a storyboard properly. After you complete this you will see the benefits of the new style and you can decide whether to do it in code or using the interface builder:
http://developer.apple.com/library/ios/#documentation/iPhone/Conceptual/SecondiOSAppTutorial/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011318