I have a UITableView with 2 text field and a button. If I run the simulator without use custom class, I can see the text fields and button:
But when i use a custom class, my UITable view only display a lot of lines without content:
Here is how I've created my properties:
LoginSceneController.h
#import <UIKit/UIKit.h>
#interface LoginSceneController : UITableViewController
#property (nonatomic, strong) IBOutlet UITextField *email;
#property (nonatomic, strong) IBOutlet UITextField *password;
- (IBAction)doLogin;
#end
LoginSceneController.m
#import "LoginSceneController.h"
#interface LoginSceneController ()
#end
#implementation LoginSceneController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (void)doLogin {
NSURL *url = [NSURL URLWithString:#"http://rest-service.guides.spring.io/greeting"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response,
NSData *data, NSError *connectionError)
{
if (data.length > 0 && connectionError == nil)
{
NSDictionary *greeting = [NSJSONSerialization JSONObjectWithData:data
options:0
error:NULL];
self.email.text = [[greeting objectForKey:#"id"] stringValue];
self.password.text = [greeting objectForKey:#"content"];
}
}];
}
#end
The problem happens when I use a custom class (or referencing outlet or add a send event on button).
What is wrong?
edit: I think that I need populate my interface using my custom class because the static content is being lost. Is it possible to be the cause of content being lost?
You have two options when it comes to UITableView and Interface Builder. You can have a dynamic table view (pretty common) where your code overrides UITableViewController methods like "numberOfRowsInSection" and "cellForRowAtIndexPath". The other option is a static tableview, and that seems like what you want to do (especially since you haven't overridden the two aforementioned methods, and leads to your blank table). My guess is you need to select "static" for the tableview as shown in the third screenshot in this tutorial.
Related
I'm new to iOS development and to stack overflow. I need to show user's data in multiple UILabel and UITextField, the data is obtained from a POST method. There is a slight delay for getting the data from the server. How to reload or populate the elements after getting the details.
This is my viewDidLoad()
#interface EditProfileViewController ()
#property (weak, nonatomic) IBOutlet UIImageView *displayPictureView;
#property (weak, nonatomic) IBOutlet UITextField *firstnameFeld;
#property (weak, nonatomic) IBOutlet UITextField *lastNameField;
#property (weak, nonatomic) IBOutlet UITextField *emailField;
#property (weak, nonatomic) IBOutlet UITextField *bdayField;
#property (weak, nonatomic) IBOutlet UIButton *calField;
#property (weak, nonatomic) IBOutlet UITextView *addressTextView;
#property (weak, nonatomic) IBOutlet UILabel *userIDLabel;
#property (weak, nonatomic) IBOutlet UILabel *phoneNumberLabel;
#property (weak, nonatomic) IBOutlet UIScrollView *editScrollView;
#end
#implementation EditProfileViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.editScrollView.delegate=self;
}
I receive data from the server using the code, i have this POST method in viewDidLoad()
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
NSMutableURLRequest * urlRequest = [NSMutableURLRequest requestWithURL:urlEdit];
[urlRequest setHTTPMethod:#"POST"];
[urlRequest setHTTPBody:[editParameters dataUsingEncoding:NSUTF8StringEncoding]];
NSURLSessionDataTask * dataTask =[defaultSession dataTaskWithRequest:urlRequest
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(#"Response:%# \nError:%#\n", response, error);
if(error == nil)
{
NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog(#"DataText = %#",text);
}
NSError *error2 = nil;
jsonDicEditAcc = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error2];
if (error2 != nil) {
NSLog(#"Error parsing JSON.");
}
else {
NSLog(#"JsonDictEditAcc: \n%#",jsonDicEditAcc);
[self dismissViewControllerAnimated:alert completion:nil];}
}];
[dataTask resume];
I will extract data from the "jsonDicEditAcc", i need to know how to populate the UILabels and UITextField, after getting the data because the view gets loaded before getting the data. I have tried viewWillAppear() did not work. Help me out.. Thanks in advance..
You'll need to know the content of the data you're trying to apply. If you know the key's that associate with each value you can apply those values to the text fields (and labels and buttons) and be done. They will change when you successfully update them.
Inside your block you need to trigger the population of the text fields. You can add something like this:
else {
NSLog(#"JsonDictEditAcc: \n%#",jsonDicEditAcc);
[self updateLabelsAndTextFields];
[self dismissViewControllerAnimated:alert completion:nil];}
}];
This function that will extract the data from jsonDicEditAcc and update the text of each relevant UILabel. The view has already loaded, but the UILabel will update when you change the text.
A solution could look like this:
- (void)updateLabelsAndTextFields{
self.firstnameFeld.text = jsonDicEditAcc[#"key"];
self.lastNameField.text = jsonDicEditAcc[#"key"];
self.emailField.text = jsonDicEditAcc[#"key"];
self.bdayField.text = jsonDicEditAcc[#"key"];
self.userIDLabel.text = jsonDicEditAcc[#"key"];
[calField setTitle:jsonDicEditAcc[#"key"] forState:UIControlStateNormal];
}
Be sure name a UIButton a button, i.e. "calButton" not "calField"
It's pretty straightforward, add similar code to the else branch of your if (error2 != nil) statement:
weakSelf.firstnameFeld = jsonDicEditAcc[#"<key-for-firstname>"];
Also, you might find it necessary to remove this statement from that branch as it will close the current viewController:
[self dismissViewControllerAnimated:alert completion:nil];
And I might point out, you need to use weakSelf inside of blocks to prevent memory leaks. Using self inside a block creates a retain cycle which means your entire viewController remains allocated until iOS eventually crashes your app for excessive memory use.
Put this at the top of your method and it is accessible inside the block, and then change all references of self inside of blocks to weakSelf.
typeof(self) __weak weakSelf = self;
I've built a small demo-application which allows the user to choose a color, which is sent to a basic (for now localhost) node.js server (using NSURLSessionDataTask), which uses the color name to get a fruit name and image URL, and return a simple 2 property JSON object containing the two.
When the application receives the JSON response, it creates a sentence with the color name and fruit name to display in the GUI, and then spawns another NSURLSession call (this time using NSURLSessionDownloadTask) to consume the image URL and download a picture of the fruit to also display in the GUI.
Both of these network operations use [NSURLSession sharedSession].
I'm noticing that both the JSON call and more noticeably the image download are leaking significant amounts of memory. They each follow a similar pattern using nested blocks:
Initialize the session task, passing a block as the completion handler.
If I understand correctly, the block is run on a separate thread since the communication in NSURLSession is async by default, so updating the GUI has to happen in the main, so within the completeHandler block, a call to dispatch_async is made, specifying the main thread, and a short nested block that makes a call to update the GUI.
My guess is that either my use of nested blocks, or nesting of GCD calls is causing the issue. Though it's entirely possible my problem is multi-faceted.
Was hoping some of you with more intimate knowledge of how Obj-C manages memory with threads and ARC would be greatly helpful. Relevant code is included below:
AppDelegate.m
#import "AppDelegate.h"
#import "ColorButton.h"
#interface AppDelegate ()
#property (weak) IBOutlet NSWindow *window;
#property (weak) IBOutlet NSImageView *fruitDisplay;
#property (weak) IBOutlet NSTextField *fruitNameLabel;
#property (weak) IBOutlet ColorButton *redButton;
#property (weak) IBOutlet ColorButton *orangeButton;
#property (weak) IBOutlet ColorButton *yellowButton;
#property (weak) IBOutlet ColorButton *greenButton;
#property (weak) IBOutlet ColorButton *blueButton;
#property (weak) IBOutlet ColorButton *purpleButton;
#property (weak) IBOutlet ColorButton *brownButton;
#end
#implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
proxy = [[FruitProxy alloc] init];
}
- (void)applicationWillTerminate:(NSNotification *)aNotification
{
// Insert code here to tear down your application
}
-(BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender
{
return YES;
}
/*------------------------------------------------------------------*/
- (IBAction)colorButtonWasClicked:(id)sender
{
ColorButton *btn = (ColorButton*)sender;
NSString *selectedColorName = btn.colorName;
#autoreleasepool {
[proxy requestFruitByColorName:selectedColorName
completionResponder:^(NSString* fruitMessage, NSString* imageURL)
{
[self fruitNameLabel].stringValue = fruitMessage;
__block NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
__block NSURLSession *imageSession = [NSURLSession sharedSession];
__block NSURLSessionDownloadTask *imgTask = [imageSession downloadTaskWithRequest:req
completionHandler:
^(NSURL *location, NSURLResponse *response, NSError *error)
{
if(fruitImage != nil)
{
[self.fruitDisplay setImage:nil];
fruitImage = nil;
}
req = nil;
imageSession = nil;
imgTask = nil;
response = nil;
fruitImage = [[NSImage alloc] initWithContentsOfURL:location];
[fruitImage setCacheMode:NO];
dispatch_async
(
dispatch_get_main_queue(),
^{
[[self fruitDisplay] setImage: fruitImage];
}
);
}];
[imgTask resume];
}];
}
}
#end
FruitProxy.m
#import "FruitProxy.h"
#implementation FruitProxy
- (id)init
{
self = [super init];
if(self)
{
return self;
}
else
{
return nil;
}
}
- (void) requestFruitByColorName:(NSString*)colorName
completionResponder:(void( ^ )(NSString*, NSString*))responder
{
NSString *requestURL = [self urlFromColorName:colorName];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:requestURL]];
session = [NSURLSession sharedSession];
#autoreleasepool {
NSURLSessionDataTask *task = [session dataTaskWithRequest:request completionHandler:
^(NSData *data, NSURLResponse *response, NSError *connectionError)
{
NSString *text = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSDictionary *responseObj = (NSDictionary*)[NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSString *fruitName = (NSString*)responseObj[#"fruitName"];
NSString *imageURL = (NSString*)responseObj[#"imageURL"];
NSLog(#"Data = %#",text);
dispatch_async
(
dispatch_get_main_queue(),
^{
responder([self messageFromColorName:colorName fruitName:fruitName], imageURL);
}
);
}];
[task resume];
}
}
- (NSString*)urlFromColorName:(NSString*)colorName
{
NSString *result;
result = #"http://localhost:9000/?color=";
result = [result stringByAppendingString:colorName];
return result;
}
- (NSString*)messageFromColorName:(NSString*)colorName
fruitName:(NSString*)fruitName
{
NSString *result = #"A ";
result = [[[[result stringByAppendingString:colorName]
stringByAppendingString:#"-colored fruit could be "]
stringByAppendingString:fruitName]
stringByAppendingString:#"!"];
return result;
}
#end
Where does "fruitImage" come from in AppDelegate.m? I don't see it declared.
the line:
__block NSURLSessionDownloadTask *imgTask
is a bit weird because you're marking imgTask as a reference that can change in the block, but it's also the return value. That might be part of your problem, but in the very least it's unclear. I might argue that all the variables you marked __block aren't required to be as such.
typically a memory leak in these situations is caused by the variable capture aspect of the block, but I'm not seeing an obvious offender. The "Weak Self" pattern might help you here.
Using "leaks" might help you see what objects are leaking, which can help isolate what to focus on, but also try to take a look at your block's life cycles. If a block is being held by an object it can create cycles by implicitly retaining other objects.
Please follow up when you figure out exactly what's going on.
reference:
What does the "__block" keyword mean?
Always pass weak reference of self into block in ARC?
i'm trying to make a subclass of NSURLConnection where i have an additional property (in this case "connectionName") to help me distinguish between 2 different connections.
i created the subclass, named it CustomURLConnection and gave it the property "connectionName".
then in my file ImagesViewController.m (which is an UICollectionView) i import the header CustomURLConnection and try to give the connections a name and retrieve it afterwards, but it doesn't work, as soon as i enter this collection view the app crashes and gives me the following error:
-[NSURLConnection setConnectionName:]: unrecognized selector sent to instance 0x1090a40f0
Here is some code: (if you want, here's a CLEARER IMAGE)
CustomURLConnection.h
#import <Foundation/Foundation.h>
#interface CustomURLConnection : NSURLConnection
#property (strong, nonatomic) NSString *connectionName;
#end
ImagesViewController.h
#import <UIKit/UIKit.h>
#interface ImagesViewController : UICollectionViewController<NSURLConnectionDelegate>
#property (strong, nonatomic) UIImageView *imageView;
#end
ImagesViewController.m
...
#import "CustomURLConnection.h"
#interface ImagesViewController (){
NSArray *contentStrings;
NSMutableData *contentData; // Holds data from the initial load
NSMutableData *contentImageData; // Holds data when the user scrolls up/down in the collection view
}
#end
...
-(void)loadInitialData{ // Loads data from page
NSString *hostStr = #"http://www.website.com/example";
NSURL *dataURL = [NSURL URLWithString:hostStr];
NSURLRequest *request = [NSURLRequest requestWithURL:dataURL];
CustomURLConnection *connectionData = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self]; // Make connection
connectionData.connectionName = #"InitialData"; // Give it a name
}
...
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
// Do some stuff
NSString *hostStr = #"http://www.website.com/example2";
_imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0,0,100,100)];
[imageCell addSubview:_imageView]; // Adds an image view to each collection cell
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:hostStr]];
CustomURLConnection *connectionImg = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self]; // Make connection
connectionImg.connectionName = #"ImageData"; // Give it a different name than before
// Do some stuff
return imageCell;
}
...
// Here are the main methods for the connections
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
if([((CustomURLConnection *)connection).connectionName isEqualToString:#"InitialData"]){
contentData = [[NSMutableData alloc] init];
}
else{
contentImageData = [[NSMutableData alloc] init];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
if([((CustomURLConnection *)connection).connectionName isEqualToString:#"InitialData"]){
[contentData appendData:data];
}
else{
[contentImageData appendData:data];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{
if([((CustomURLConnection *)connection).connectionName isEqualToString:#"InitialData"]){
// Do some stuff
}
else{
UIImage *image = [[UIImage alloc] initWithData:contentImageData];
_imageView.image = image;
}
}
am i missing something? i came across this error many times before but the causes are never the same and this time i can't seem to find a solution on my own.
hopefully you can see what i'm doing wrong and help me :)
thanks.
EDIT: turns out there is a better way to achieve my goal, have a look here
Thank again to everyone for the help :)
CustomURLConnection *connectionImg = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self]; // Make connection
creates an NSURLConnection object. Casting to CustomURLConnection does not change
the class of this object. Replace that line with
CustomURLConnection *connectionImg = [CustomURLConnection connectionWithRequest:request delegate:self]; // Make connection
to create an instance of your subclass.
In your delegate methods change NSURLConnection by CustomURLConnection, for instance :
- (void)connection:(CustomURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
and when you create it just do :
CustomURLConnection *connectionImg = [[CustomURLConnection alloc] initWithRequest:request delegate:self];
connectionImg.connectionName = #"ImageData"; // Give it a different name than before
In this line:
CustomURLConnection *connectionData = (CustomURLConnection *)[NSURLConnection connectionWithRequest:request delegate:self];
you are creating an instance of NSURLConnection, not CustomURLConnection. So, when you cast the result to CustomURLConnection * you are lying to the compiler.
Later, at runtime, when you try to use a feature of CustomURLConnection you get an exception because your connection is the wrong class type and doesn't implement the method.
You need to instantiate CustomURLConnection, not NSURLConnection.
Adding to the other good answers here, your CustomURLConnection class should override +connectionWithRequest:delegate: to return an instance of CustomURLConnection, like this:
+(CustomURLConnection*)connectionWithRequest:(NSURLRequest*)request delegate:(id)delegate
{
return [[CustomURLConnection alloc] initWithRequest:request delegate:delegate];
}
That lets you use the same style you had:
CustomURLConnection *connectionData = [CustomURLConnection connectionWithRequest:request delegate:self]; // Make connection
More importantly, a user of your code (most likely the future you) might assume that sending +connectionWithRequest:delegate: to CustomURLConnection would return an instance of CustomURLConnection. Without the override, they'll get an instance of NSURLConnection instead, and that's a difficult bug to spot.
Ive been stuck on this problem for a good while now, I read a bunch of threads but none describe my problem I tried a whole bunch of different methods to do it but none worked. I have a PFFile that I pulled from array and sent through a segue to a download detail view. This file is called "download file".I am trying to program a button when clicked to initiate the download. here is the code:
this is my download detail.h
#import <UIKit/UIKit.h>
#import <Parse/Parse.h>
#interface PDFDetailViewController : UIViewController {
}
#property (strong, nonatomic) IBOutlet UILabel *PDFName;
#property (strong, nonatomic) IBOutlet UILabel *PDFDescription;
#property (strong, nonatomic) NSString* PDFna;
#property (strong, nonatomic) NSString* PDFdes;
#property (retain,nonatomic) PFFile * downloadfile;
- (IBAction)Download:(id)sender;
#end
my download detail button
- (IBAction)Download:(id)sender {
[self Savefile];
}
-(void) Savefile {
NSData *data = [self.downloadfile getData];
[data writeToFile:#"Users/Danny/Desktop" atomically:NO];
NSLog(#"Downloading...");
}
#end
and here is the segue that sends the download file:
detailVC.downloadfile=[[PDFArray objectAtIndex:indexPath.row]objectForKey:#"PDFFile"];
I get the array data using the PFQuery and store it into "PDFArray". This is a synchronous download because a warning message comes up when i click the button saying that main thread is being used. Although the file doesn't show up on my desktop.
Have you tried using this Parse method?
getDataInBackgroundWithBlock:
-(void) Savefile {
[self.downloadfile getDataInBackgroundWithBlock:^(NSData *data, NSError *error) {
if (error) {
// handle error
}
else if (data) {
[data writeToFile:#"Users/Danny/Desktop" atomically:NO];
}
}];
}
I am very new to IOS. With the help of another managed to 'get' data from a third-party API and display in my table view, but now l want to write a number of strings from UIText fields, format based on the API docs and write to the server. The existing code that l have uses blocks. Can anyone provide a very simple example?
Below serves as a rough stab in the dark at decomposing what would be required:
Class 1 - VC class:
//User input from UIText field
I am assuming that l need to write the strings to a NSDictionary- object and key pairs, and using a block?
Call a method which resides in Class 2, which will request the data from the block in Class 1?
Class 2 - Model class:
//Format the data as per the API dictates
//'PUT' the string to the server
Here is what l have thus far:
URLOUTViewController.h Class
//
// URLOUTViewController.h
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "URLOUT_Model.h"
#interface URLOUTViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *firstName;
#property (strong, nonatomic) IBOutlet UITextField *secondName;
- (IBAction)submitButton:(id)sender;
#end
URLOUTViewController.m
//
// URLOUTViewController.m
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import "URLOUTViewController.h"
#import "URLOUT_Model.h"
#interface URLOUTViewController ()
//Private properties
#property (strong, nonatomic) URLOUT_Model *modelNewDroplet;
#property (strong, nonatomic) NSDictionary *userInput;
#end
#implementation URLOUTViewController
#synthesize userInput;
#synthesize firstName;
#synthesize secondName;
- (URLOUT_Model *) modelNewDroplet
{
if (!_modelNewDroplet) _modelNewDroplet = [[URLOUT_Model alloc]init];
return _modelNewDroplet;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[URLOUT_Model createDropletWithCompletion:^(NSDictionary *userInput) {
self.modelNewDroplet = userInput;
}];
// 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)submitButton:(id)sender {
NSDictionary *dic = [[NSDictionary alloc] initWithObjectsAndKeys:
self.firstName.text, #"firstName", self.secondName.text, #"secondname",nil];
NSLog(#"%#", [dic objectForKey:#"firstName, secondName"]);
}
#end
URLOUT_Model.h
//
// URLOUT_Model.h
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "URLOUTViewController.h"
typedef void (^NSDictionayBlock)(NSDictionary * params);
#interface URLOUT_Model : NSObject
+ (void)createDropletWithCompletion:(NSDictionary *) params;
#end
URLOUT_Model.m
//
// URLOUT_Model.m
// URLOUT
//
// Created by Gregory Arden on 21/11/2013.
// Copyright (c) 2013 Not Defined. All rights reserved.
//
#import "URLOUT_Model.h"
#implementation URLOUT_Model
+ (void)createDropletWithCompletion:(NSDictionary *) params;
{
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:#{params: firstName, secondName, nil}];
[urlRequest setHTTPMethod:#"PUT"];
[urlRequest setCachePolicy:NSURLCacheStorageNotAllowed];
[urlRequest setTimeoutInterval:30.0f];
[urlRequest addValue:#"application/json" forHTTPHeaderField:#"Content-Type"];
[NSURLConnection sendAsynchronousRequest:urlRequest
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *responseData, NSError *error){
NSError *serializationError = nil;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingAllowFragments
error:&serializationError];
}];
}
#end
Creating a HTTP PUT request is simple:
Create an instance of NSMutableURLRequest.
Set the HTTP method to #"PUT" using the -setHTTPMethod: method.
Set the header fields and body using appropriate methods (-setValue:forHTTPHeaderField:, -setHTTPBody:, etc.)
Send the request using an instance of NSURLConnection.
Where the data that you send in the request comes from (text fields, data files, random number generator...) really doesn't make a difference in the creation of the request. You really shouldn't be storing data in your views, though. Generally speaking, your text views and such should trigger an action in a view controller when they change, and the view controller should update the data model. When you want to send data to some server, pull the data from the model, not from UI components.