How to display Web Service data on iOS

Source: Internet
Author: User
Tags call back

In iOS development, you need to interact with the WEB server, such as displaying a batch of data from the web service on a table. The Data Interaction format is XML, and the protocol used is SOAP. The requested data contains images. Generally, the images are reconnected by a URL. You need to obtain the URL and download it to the terminal for display.

If you are using a browser, it's all done. But if you want to display and process the data more flexibly, You need to develop an application.

1. Implementation Process
I have created a simple view controller-based application. The newly created View Controller class XYViewController.

Manually add the UITableView object resultTableView to this class to display the data requested by the WEB Service. Web service uses the SOAP protocol for interaction. Create a Data Request class XYQueryHotel and use its delegate to call back the data in the form of an array.

In this data request class, the asynchronous request data is used to analyze the received XML format data using the NSXMLParser class.

When the View Controller type XYViewController requests data, a wait is inevitable, but the UI can continue because it is an asynchronous request operation. In this example, you can set some interesting pictures for killing time to avoid boring waiting and improve UI friendliness.

After the data request operation is complete, the data is returned to the View Controller XYViewController by delegate. The View Controller class XYViewController displays the data of this attribute in the UITableView object resultTableView.

For resultTableView, data can be set on the cell displayed on the screen by using the datasource method (UITableViewCell *) tableView :( UITableView *) tableView cellForRowAtIndexPath :( NSIndexPath *) indexPath.

In resultTableView, another image information needs to be displayed, which needs to be sent to the web service through the image URL in resultHotels.

This process also needs to be implemented asynchronously, including the requested image uiimage data, the requested data optimization, the requested data presentation, and other operations, which cannot affect the UI anyway. This is the key point and difficulty in the entire implementation process.

This request is sent when data is loaded at the beginning. (UITableViewCell *) tableView :( UITableView *) tableViewcellForRowAtIndexPath :( NSIndexPath *) indexPath. In this method, multiple threads are used to send requests when the cell is filled. Its implementation code is as follows:

 

-(UITableViewCell *) tableView :( UITableView *) tableView cellForRowAtIndexPath :( NSIndexPath *) indexPath {

Static NSString * CellIdentifier = @ "resultCell ";

// Initialize the cell and specify its type. You can also customize the cell.

Repeated Cell = (xy1_cell *) [tableView dequeueReusableCellWithIdentifier: CellIdentifier];

If (cell = nil)

{

// Load all objects in M. xib

NSArray * nib = [[NSBundle mainBundle] loadNibNamed: @ "KaiFangTableViewCell" owner: nil options: nil];

// The first object is CustomCell.

Cell = [nib objectAtIndex: 0];

}

Switch (indexPath. section ){

Case 0: // corresponding Partition

// Modify the mcell Control

Raise info = [resultHotelsobjectAtIndex: indexPath. row];

Cell. name. text = [hotelInfoname];

Cell. addr. text = [hotelInfoaddr];

Cell. distance. text = [NSStringstringWithFormat: @ "% @, % @", [using info lat], [using info lng];

If (cmdinfo. hasLogoImage)

{

Cell. logo. image = parameter info. logo;

}

Else

{

Cell. logo. image = [UIImageimageNamed: @ "Placeholder.png"];

If (! ResultTableView. dragging &&! ResultTableView. decelerating ){

[SelfstartOperationsForHotelLogo: hotelInfo atIndexPath: indexPath];

}

}

// Return CustomCell

Return multicast cell;

Break;

}

Return cell; // return cell

}

 

In the array resultHotels, records of objects with the same kind of information are saved. CATEGORY info is a data class defined separately and belongs to the MODE category in MVC.

Method tableView: cellForRowAtIndexPath: dynamically retrieves records of corresponding rows in the Array Based on the cell row number displayed on the screen. Then, enter the obtained record information in the cell for display. Then, use the [self startoperationsforationlogo: hotelInfoatIndexPath: indexPath] method to display the image in a multi-thread request.

 

In this method, [self startOperationsForHotelLogo: hotelInfoatIndexPath: indexPath] includes the operations for downloading images from the web service, beautifying images, and displaying images to corresponding cells, modify the information in the submitted record, and do not need to request this method again when it is displayed again, such as Scrolling up or down.

 

If the user exits this View Controller class, how can the resultHotels array be cached? How can the web service interaction be no longer performed.

 

Continue to implement the [selfstartOperationsForHotelLogo: hotelInfo atIndexPath: indexPath] method,

 

-(Void) startOperationsForHotelLogo :( paiinfo *) hotel atIndexPath :( NSIndexPath *) indexPath {

If (! Hotel. hasLogoImage ){

[SelfstartImageDownloadingForHotelLogo: hotel atIndexPath: indexPath];

}

If (! Record. isFiltered ){

[Self startImageFiltrationForRecord: recordatIndexPath: indexPath];

}

}

Determine whether to download the object based on hasLogoImage, and then decide whether to beautify the object based on isFiltered.

 

Continue to implement (void) startImageDownloadingForHotelLogo :( paiinfo *) hotel atIndexPath :( NSIndexPath *) indexPathWithLogo method.

 

-(Void) startImageDownloadingForHotelLogo :( paiinfo *) hotelatIndexPath :( NSIndexPath *) indexPathWithLogo {

If (self. pendingOperations = nil)

{

Self. pendingOperations = [[PendingOperationsalloc] init];

NSLog (@ "pendingOperationsinitial ");

}

If (! [Self. pendingOperations. downloadsInProgress. allKeys containsObject: indexPathWithLogo]) {

Export imageDownloader * imageDownloader = [[Export imageDownloader alloc] initWithHotel: hotel atIndexPath: indexPathWithLogo delegate: self];

 

[Self. pendingOperations. downloadsInProgress setObject: imageDownloaderforKey: indexPathWithLogo];

[Self. pendingOperations. downloadQueueaddOperation: imageDownloader];

NSLog (@ "operation count is % d", [self. pendingOperations. downloadQueue operationCount]);

}

}

 

First, assign and initialize an object pendingOperations, which is used to save the NSMutableDictionary object of the Operation queue and the NSOperationQueue object of the operation. The former stores the operations in the Operation queue, and then runs the operation in multiple threads. If the multi-threaded operation is canceled, delete the operation in the Operation queue.

 

Next, judge that the corresponding record of this indexPath is not in the download queue. If yes, no further operations will be performed. If not, perform the download operation.

 

The implementation of this download operation is a key point. It requires multi-threaded implementation and is fed back to the main thread after completion. There are many implementation methods for multithreading. As far as I know, there are three methods: NSThread, NSOperation, and Grand Central Dispatch (GCD ).

 

The implementation method of GCD is used to implement simple logic. I don't know how to call delegate.

 

Dispatch_queue_timageQueue = dispatch_queue_create ("invalid info. logo. imageQueue", NULL );

Dispatch_async (imageQueue, ^ {

NSData * imageData = [NSDatadataWithContentsOfURL: [hotel logoURL];

Dispatch_async (dispatch_get_main_queue (), ^ {

[Raise info setLogo: [UIImageimageWithData: imageData];

[ResultTableView reloadRowsAtIndexPaths: [NSArrayarrayWithObject: indexPathWithLogo] withRowAnimation: UITableViewRowAnimationNone];});

});

 

NSOperation is encapsulated based on GCD and is easier to use. To implement simple logic, you only need to use block. To implement complex logic, you only need to use NSOperation as the parent class and rewrite the main () method. NSOperation is an abstract class. (I do not know what abstract classes are)

 

I have never used NSThread implementation method. I will not introduce it.

 

Here, we use the NSOperation subclass javasimagedownloader to perform the download operation, and then add the definition object of the javasimagedownloader class to the NSOperationQueue to implement the background download operation.

Upload the producer info, indexPath, and delegate to this object. These are the necessary conditions for the operation to be implemented, and call back to the main thread using delegate to update the UI.

 

Export imageDownloader * imageDownloader = [[Export imageDownloader alloc] initWithHotel: hotelatIndexPath: indexPathWithLogo delegate: self];

 

The main functions of HotelImageDownloader are defined as follows:

# Pragmamark-

# Pragmamark-Life Cycle

 

-(Id) initWithHotel :( paiinfo *) hotel atIndexPath :( NSIndexPath *) indexPathdelegate :( id <your imagedownloaderdelegate>) theDelegate

{

If (self = [super init]) {

// Set the properties.

Self. delegate = theDelegate;

Self. indexPathInTableView = indexPath;

Self. Privacy info = hotel;

}

Return self;

}

 

# Pragmamark-

# Pragmamark-Downloading image

 

// 3: Regularly check for isCancelled, to make sure the operation terminates as soonas possible.

-(Void) main {

// 4: Apple recommends using @ autoreleasepool block instead of alloc and init nutoreleasepool, becauseblocks are more efficient. You might use NSAuoreleasePool instead and thatwocould be fine.

If (self. isCancelled)

Return;

NSData * imageData = [[NSData alloc] initWithContentsOfURL: self. Your info. logoURL];

If (self. isCancelled ){

ImageData = nil;

Return;

}

If (imageData ){

UIImage * downloadedImage = [UIImageimageWithData: imageData];

Self. cmdinfo. logo = downloadedImage;

}

Else {

Self. failed info. failed = YES;

}

ImageData = nil;

If (self. isCancelled)

Return;

// NSLog (@ "performSelectorOnMainThread ");

// 5: Cast the operation to NSObject, andpolicy the caller on the main thread.

[(NSObject *) self. delegateperformselecw.mainthread: @ selector (logoImageDownloaderDidFinish :) withObject: self waitUntilDone: NO];

}

 

Code [(NSObject *) self. delegate implements mselecw.mainthread: @ selector (logoImageDownloaderDidFinish :) withObject: self waitUntilDone: NO]; implements callback to the main thread, and calls the imageDownloader Class Object imageDownloader through the parameter item "withObject: self "is passed back, And self is the imageDownloader object. Its attributes include the latest info information and indexPath information. Use this information to update the response results of web service requests and the cell corresponding to UITABLEVIEW (that is, to update uiimage ).

 

This operation is implemented using delegate, and some people also use nsicationicationcenter.

 

The general implementation process is as follows.

First, add a notification to the viewDidLoad method in the XYViewController class. The name is @ "Your logodownloader. completed ". When this notification is triggered, a method "logoImageDownloaderDidFinish:" is called :". This method is required in the delegate.

[[Nsnotifcencenterdefacenter] addObserver: selfselector: @ selector (logoImageDownloaderDidFinish :) name: @ "Your logodownloader. completed" object: nil];

Next, add a notification sending code in the main () method of the receivimagedownloader class, which is the same as calling the delegate operation here.

 

NSDictionary * userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [Define objectforkey: @ "code"], @ "code", nil];

[[Nsnotifcencenterdefacenter] postNotificationName: @ "Your logodownloader. completed" object: self userInfo: userInfo];

The parameter item "object: self" of the notification submission operation is passed back. self is the imageDownloader object, and its attributes include the latest info information and indexPath information.

In this way, the message transmission mechanism is also implemented.

 

Both methods are used to transmit messages to the main thread, which is a cross-thread, cross-method message transfer operation.

 

Regardless of the method used, you must define the "logoImageDownloaderDidFinish:" method in the View Controller class XYViewController of the main thread.

-(Void) logoImageDownloaderDidFinish :( implements imagedownloader *) downloader {

NSLog (@ "logoImageDownloaderDidFinishis executed ");

 

NSIndexPath * indexPath = downloader. indexPathInTableView;

Using info * theHotel = downloader. Using Info;

[Using resultsreplaceobjectatindex: indexPath. row withObject: theHotel];

 

[ResultTableView reloadRowsAtIndexPaths: [NSArrayarrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationFade];

[Self. pendingOperations. downloadsInProgressremoveObjectForKey: indexPath];

}

This method is used to update the response results of the web service request result set and the cell corresponding to the UITABLEVIEW (that is, to update the uiimage ), at the end of the download queue, delete the key and value from the dictionary.

The implementation code of this logic is the last three lines.

Code [revoke resultsreplaceobjectatindex: indexPath. row withObject: theHotel]; records of the corresponding row of the array 'distribute result' are updated based on the row number.

Code [resultTableView reloadRowsAtIndexPaths: [NSArrayarrayWithObject: indexPath] withRowAnimation: UITableViewRowAnimationFade]; call the datasource method of uitableview.

This method reloads the UITableViewCell instance in the specified indexPaths. Because the cell is reloaded, the system requests the data source of the UITableView instance to obtain the new cell; this table uses the animation effect to bring the new cell into and exit the old cell. Call all methods in the UITableViewDataSource protocol to update the data source. Call tableView (UITableViewCell *) tableView :( UITableView *) tableViewcellForRowAtIndexPath :( NSIndexPath *) indexPath to call only the number of rows to be updated, to obtain the new cell. In this case, the cell's-(void) setSelected :( BOOL) selected animated :( BOOL) animated will be called and the set selected is NO;

However, I don't know whether to update only the cells displayed on the screen or all the cells displayed on the screen will be updated.

 

If all the records of the results are displayed on the screen, the results will be returned here. However, it is a pity that this is not a PC terminal and it is not as big as a screen. That is, the PC terminal also has a lazy load function. It is not so easy to request all the data at once. It is also a screen full, and the rest of the content will be displayed in the drop-down list. This is based on performance considerations. On iphone terminals, this demand is more intense. It is impossible for an application to write all the records of the results into the UI memory first, but to write the rows displayed on the screen into the screen memory.

 

As a result, a new problem arises.

 

Because the iphone screen will scroll up and down, this is the most basic function of uitableview. During the scroll up/down operation, the results record displayed on the screen changes.

For example, 1-10 rows of records are initially displayed, and 5-15 rows are displayed after the drop-down. In this case, we should stop downloading the logoImg information recorded in the first five rows. The original download operation queue needs to be canceled, and the download operations recorded from 11 to 15 are added. If you download the forward record and reloadRowsAtIndexPaths: withRowAnimation: operation, the operation fails. Obviously, there is no way to update the screen.

This rolling operation has the delegate implementation of uiscrollview. What we need to do is to implement the delegate method in the main View Controller class XYViewController.

 

# Pragmamark-

# Pragmamark-UIScrollView delegate

 

-(Void) scrollViewWillBeginDragging :( UIScrollView *) scrollView {

[Self. pendingOperations. downloadqueuesetsuincluded: YES];

}

 

-(Void) scrollViewDidEndDragging :( UIScrollView *) scrollViewwillDecelerate :( BOOL) decelerate {

If (! Decelerate ){

[Self loadImagesForOnscreenCells];

[Self. pendingOperations. downloadqueuesetsuincluded: NO];

}

}

 

-(Void) scrollViewDidEndDecelerating :( UIScrollView *) scrollView {

// 3: This delegate method tells you thattable view stopped scrolling, so you will do the same as in #2.

NSLog (@ "scrollViewDidEndDecelerating ");

[Self loadImagesForOnscreenCells];

// [DownloadLogoQueue setsuincluded: NO];

[Self. pendingOperations. downloadqueuesetsuincluded: NO];

}

 

 

 

# Pragmamark-

# Pragmamark-Cancelling, suspending, resuming queues/operations

 

-(Void) loadImagesForOnscreenCells {

NSSet * visibleRows = [NSSetsetWithArray: [resultTableView indexPathsForVisibleRows]; // lines that are displayed on the screen, for example, 6-15

NSMutableSet * pendingOperations = [NSMutableSet setWithArray: [self. pendingOperations. downloadsInProgressallKeys]; // 1-8th row of the queue being downloaded.

NSMutableSet * toBeCancelled = [pendingOperations mutableCopy];

NSMutableSet * toBeStarted = [visibleRowsmutableCopy];

[ToBeStarted minusSet: pendingOperations]; // currently, lines 6-15 are displayed on the screen, minus 1-8 of the queue being downloaded. The row to be added to the download queue.

[ToBeCancelled minusSet: visibleRows]; // download queue lines 1-8, minus lines that are displayed on the screen, for example, 6-15 lines. To cancel the download queue.

For (NSIndexPath * anIndexPath describecancelled ){

Required imagedownloader * pendingDownload = [self. pendingOperations. downloadsInProgress objectForKey: anIndexPath];

[PendingDownload cancel]; // call [NSOperationcancel]; cancel the operation.

[Self. pendingOperations. downloadsInProgressremoveObjectForKey: anIndexPath];

}

ToBeCancelled = nil;

For (NSIndexPath * anIndexPath describestarted ){

Using info * using toprocess = [using infosobjectatindex: anIndexPath. row];

[Selfstartoperationsforprocess logo: hotelToProcess atIndexPath: anIndexPath]; // execute multi-threaded download, beautification, and other operations for image display.

}

ToBeStarted = nil;

}

 

Method [selfstartOperationsForHotelLogo: effectoprocess atIndexPath: anIndexPath] is also called in "(UITableViewCell *) tableView :( UITableView *) tableView cellForRowAtIndexPath :( NSIndexPath *) indexPath" method.

The view scroll operation refreshes the screen, that is, to call "tableView: cellForRowAtIndexPath:". So, isn't the bestarted call unnecessary here, because in tableView: cellForRowAtIndexPath: method, "startoperationsfor#logo: atIndexPath:" is the method automatically called? Although it is called here, later it will be because (! [Self. pendingOperations. downloadsInProgress. allKeyscontainsObject: indexPathWithLogo]) this condition will not be loaded into the download queue. But it is a second call.

2. Summary
Many knowledge points are involved in this logical implementation process. Among them, delegate, uitableview, nsoperation, nsoperationqueue, nsxmlparser, nsicationicationcenter, deep copy of arrays, Dictionary processing, etc,

 

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.