Chapter    7

The Product Detail Screen

In this chapter you learn how to implement the product detail screen, which includes information such as the product name, price, detail, and full-size image; the screen also includes a share menu as well as the option to buy a product or add it to a favorites list.

Build the Product Detail Screen

Figure 7-1 shows the finished UI of the product detail screen. The UI uses the UINavigationBar title to show the product’s name; the right bar button item to add the share options; and a “heart” button so users can add the product to their Favorites list. To show the product’s name and price, you use two UILabels. The product detail might be pretty lengthy, so you use a UITextView to hold its content. Finally, you use a plain button labeled “Add to Bag” to let a user add this product to the shopping bag.

9781484213186_Fig07-01.jpg

Figure 7-1. The finished product detail user interface

Here’s how you implement the UI. First, create a UIViewController subclass, EMABProductDetailViewController. In the generated header file, create a property:

@property (nonatomic, strong) EMABProduct *product;

Next, pass a selected EMABProduct instance from the previous product list to this product detail. The property you created serves this purpose. Here is the complete implementation:

In the implementation file, you need a PFImageView property to display the full-size image of the product, a UILabel to display the product’s name, a UILabel to display its price, and a UITextView to show its detail. You also need a custom button that a user can tap to add the product to the Favorites list. Here is the full code:

@interface EMABProductDetailViewController ()
@property (nonatomic, weak) IBOutlet PFImageView *fullsizeImageView;
@property (nonatomic, weak) IBOutlet UILabel *productNameLabel;
@property (nonatomic, weak) IBOutlet UILabel *productPriceLabel;
@property (nonatomic, weak) IBOutlet UITextView *detailTextView;
@property (nonatomic, weak) IBOutlet UIButton *heartButton;
@end

In the implementation, first implement the EMABProduct property setter:

-(void)setProduct:(EMABProduct *)product{
    if (_product !=product) {
        _product = product;

        [self updateUI];
    }

}

In this setter, also use a helper method, -(void)updateUI. You might need to update the whole UI on multiple occasions (which is what I usually do). Instead of handling UI updates all over the place, I use one method that is called in the -(void)viewDidLoad method as shown here:

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateUI];
}

This updateUI method does one thing for right now: it connects the model and view. Here is the full code:

-(void)updateUI{
    if (self.product.fullsizeImage) {
       self.fullsizeImageView.file = self.product.fullsizeImage;
       [self.fullsizeImageView loadInBackground];
    }

    self.productNameLabel.text = self.product.name;
    self.productPriceLabel.text = [self.product friendlyPrice];
    self.detailTextView.text = [self.product detail];
}

Next, implement the IBAction that is triggered when a user taps the “Add to Bag” button. When the button is tapped, you need to check whether the customer is a valid, registered user or just a visitor. To do so, use the Parse [PFUser currentUser] method. In the case of registered user, [PFUser currentUser] will return an instance, otherwise it will return a nil. At this point, you want to ask users to sign up for an account or log in (if they haven’t done so already). I will discuss how to handle the “Add to Bag” request in a later chapter. For now, here is how you alert the user to sign up or log in:

-(IBAction)onBag:(id)sender
{
    if (! [PFUser currentUser]) {
        [self showWarning];
    } else {
        //todo:
    }
}

In this sample code, the helper method -(void)showWarning reminds the user. You will use it again on another occasion, so it’s best to create a common method:

-(void)showWarning
{
    [[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Warning", @"Warning") message:NSLocalizedString(@"Please sign up or log in", @"") delegate:nil cancelButtonTitle:NSLocalizedString(@"Later", @"Later") otherButtonTitles: NSLocalizedString(@"OK", @"OK"), nil] show];
}

This implementation only throws a warning. A better way is to bring up the signup or login screen when the user taps “OK.”

Add Share Options

An important feature of the app is the Share option or menu. To add this feature, use UIActivityViewController. Using the UIActivityViewController is pretty straightforward to use (to learn more about it, refer to the iOS documentation). First, you create what you want to share, instantiate a UIActivityViewController instance, define what services are not used for this share purpose, and then present the instance. Figure 7-2 shows a UIActivityViewController from the user’s point of view.

9781484213186_Fig07-02.jpg

Figure 7-2. The UIActivityViewController from a user’s point of view

Here is the full code:

-(IBAction)onShare:(id)sender {

    NSString *textToShare = [NSString stringWithFormat:@"%@, %@", self.product.name, [self.product friendlyPrice]];
    NSURL *imageUrl = [NSURL URLWithString:self.product.fullsizeImage.url];

    NSArray *objectsToShare = @[textToShare, imageUrl];

    UIActivityViewController *activityVC = [[UIActivityViewController alloc] initWithActivityItems:objectsToShare applicationActivities:nil];

    NSArray *excludeActivities = @[
..........................UIActivityTypeAssignToContact,
                                   UIActivityTypeSaveToCameraRoll,
                              UIActivityTypePostToFlickr,
                                   UIActivityTypePostToVimeo];

    activityVC.excludedActivityTypes = excludeActivities;
    [self presentViewController:activityVC animated:YES completion:nil];
}

Keep in mind, this is a dramatically simplified API. All available services don’t necessarily support the data content users may want to share. You may also want to add even more content for the services you want to support.

Add a Favorites List

To implement the Favorites list feature, you need a new model class.

In Xcode, create a new PFObject subclass and name it “EMABFavoriteProduct.” This model has two properties—PFUser and EMABProduct—to represent that a user liked a product. In other words, this model class is a use case of the Parse joining tables relationship. Every time a user likes a product, a record is created in this model class. You might think this is redundant. You could use another approach; for instance, you could use a favorite array for this user. After all, there is one user but many favorite products, so using an array definitely works in this case. The advantage of using the joining tables method, however, is that the code is quite clean.

To get a user’s Favorites list, use PFQuery. Create a basic PFQuery method and customize it to your needs. Here is the complete code:

In this snippet you have two extra helper queries: +(PFQuery *)queryForCustomer:(PFUser *)customer gets a list of favorite products for a user; +(PFQuery *)queryForCustomer:(PFUser *)customer product:(EMABProduct *)product checks whether the user has a product in the Favorites list.

In to EMABConstants.h add a new NSString constant, kFavoriteProduct, and implement it in EMABConstants.m file.

NSString *const kFavoriteProduct = @"FavoriteProduct";

In the required +(NSString *)parseClassName method, use the NSString constant to represent the model’s name.

+(NSString *)parseClassName
{
    return kFavoriteProduct;
}

In the +(PFQuery *)basicQuery method, sort the result based on when it is created. You can also use the PFQuery -include: method to include the user and product data. This is required to show either the product detail or the user’s detail.

+(PFQuery *)basicQuery
{
    PFQuery *query = [PFQuery queryWithClassName:[self parseClassName]];
    [query orderByDescending:@"createdAt"];
    [query include:@"customer"];
    [query include:@"product"];
    return query;
}

In the +(PFQuery *)queryForCustomer:(PFUser *)customer method, use the PFQuery query condition method -whereKey: equalTo:

+(PFQuery *)queryForCustomer:(PFUser *)customer {
    PFQuery *query = [self basicQuery];
    [query whereKey:@"customer" equalTo:customer];
    return query;

}

Then implement +(PFQuery *)queryForCustomer:(PFUser *)customer product:(EMABProduct *)product:

+(PFQuery *)queryForCustomer:(PFUser *)customer product:(EMABProduct *)product
{
    PFQuery *query = [self queryForCustomer:customer];
    [query whereKey:@"product" equalTo:product];
    return query;
}

@end

That’s the EMABFavoriteProduct. Don’t forget to register this PFSubclass at AppDelegate by calling:

[EMABFavoriteProduct registerSublcass];

Next, go to the EMABProductDetailViewController.m file. In the -(void)viewDidLoad method, check whether the user has already liked the product, given there is a valid user.

- (void)viewDidLoad {
    [super viewDidLoad];
    [self updateUI];
   [ self checkIfFavorited];
}

The checkIfFfavorited method verifies whether there is a record on the back-end server. If true, the favorite button is disabled. You also want to check that the server is queried only if there is a valid user.

-(void)checkIfFavorited
{
    if (! [PFUser currentUser]) {
        PFQuery *fPQuery = [EMABFavoriteProduct queryForCustomer:[PFUser currentUser] product:self.product];
        [fPQuery getFirstObjectInBackgroundWithBlock:^(PFObject *object, NSError *error){
            if (!error) {
                self.heartButton.enabled = false;
            }
        }];
    }
}
@end

You also want to allow users to add the product to their Favorites lists. The “heart” button is connecting to an IBOutlet method -(IBAction)onFavorite:(id)sender.

Only a user who signed up or logged in can add products to the shopping bag, or like a product. So first you need to check whether there is a valid user. If so, create a new FavoriteProduct object, and save it to the Parse back end. Once saved successfully, alert the user with a success message. Here is a full code:

-(IBAction)onFavorite:(id)sender{
    if ( ![PFUser currentUser]) {
        [self showWarning];
    } else {
        EMABFavoriteProduct *favoriteProduct = [EMABFavoriteProduct object];
        [favoriteProduct setCustomer:[PFUser currentUser]];
        [favoriteProduct setProduct:self.product];
        [favoriteProduct saveInBackgroundWithBlock:^(BOOL success, NSError *error){
            if (!error) {
                 [SVProgressHud showSuccessWithStatus: :NSLocalizedString(@"Successfully added", @""];
            }

        }];
    }
}

Summary

In this chapter, I covered the implementation of showing a product detail, sharing the product details to different services available on a user’s phone, and adding a product to the user’s Favorites list (provided that it has not been added to the list previously). The favorite product feature is implemented using a join table model class, EMABFavoriteProduct. I also emphasized the importance of the -include: method in PFQuery for fetching the user’s data or the product’s data when fetching the user’s Favorites list.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.167.183