分类目录归档:杂谈

关于tableView的section

//
//  tggViewController.m
//  Populating a Table View with Data
//
//  Created by xushao on 1/17/14.
//  Copyright (c) 2014 Pixolity Ltd. All rights reserved.
//

#import "tggViewController.h"

@interface tggViewController ()
@property (nonatomic, strong)UITableView *tggTableView;
@end

@implementation tggViewController


- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.view addSubview:self.tggTableView];
    [self.tggTableView reloadData];
    self.tggTableView.dataSource = self;
}

-(UITableView *)tggTableView{
    if (!_tggTableView) {
        _tggTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
    }
    return _tggTableView;
}

-(NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{
    if ([tableView isEqual:self.tggTableView]) {
        return 3;
    }
    return 0;
}

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    if ([tableView isEqual:self.tggTableView]) {
        switch (section) {
            case 0:
                return 3;
                break;
            case 1:
                return 5;
                break;
            case 2:
                return 8;
                break;
        }
    }
    
    return 0;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = nil;
    if ([tableView isEqual:self.tggTableView]) {
        static NSString *identifier = @"Cell";
         cell = [tableView dequeueReusableCellWithIdentifier:identifier ];
        if (!cell) {
            cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier  ];
        }
        cell.textLabel.text = [NSString stringWithFormat:@"session is %d, row is %d", indexPath.section, indexPath.row];
        return cell;
    }
    return cell;
   
}


@end

11112

Scroll Table View

//
//  TGGViewController.m
//  PlainTable
//
//  Created by xushao on 1/17/14.
//  Copyright (c) 2014 YourDeveloper. All rights reserved.
//

#import "TGGViewController.h"

@interface TGGViewController ()
@end

@implementation TGGViewController



- (void)viewDidLoad
{
    [super viewDidLoad];
    [self loadData];
    self.tggTableView.delegate = self;
    self.tggTableView.dataSource = self;
    
}

-(void)loadData{
    [self.tggTableView reloadData];
}

-(NSMutableArray *)rowData
{
    if (!_rowData) {
        
        _rowData = [[NSMutableArray alloc] init];
        for (int i = 0; i < 100; i++) {
            [_rowData addObject:[NSString stringWithFormat:@"%d",i]];
        }
        
    }
    
    return _rowData;
}


#pragma mark UITableView delegates




- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
	return [self.rowData count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *identifier = @"TGG Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
    }
    
    cell.textLabel.text = self.rowData[indexPath.row];
    return cell;
}


- (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
    
}

- (IBAction)Bottom:(UIButton *)sender {
    NSIndexPath *bottomRow = [NSIndexPath indexPathForRow:[self.rowData count]-1 inSection:0];
    [self.tggTableView scrollToRowAtIndexPath:bottomRow atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}

- (IBAction)top:(UIButton *)sender {
    
    NSIndexPath *topRow = [NSIndexPath indexPathForRow:0 inSection:0];
    [self.tggTableView scrollToRowAtIndexPath:topRow atScrollPosition:UITableViewScrollPositionTop animated:YES];

}



@end

NSObject method

/*	NSObject.h
	Copyright (c) 1994-2012, Apple Inc. All rights reserved.
*/

#ifndef _OBJC_NSOBJECT_H_
#define _OBJC_NSOBJECT_H_

#if __OBJC__

#include 
#include 

@class NSString, NSMethodSignature, NSInvocation;

@protocol NSObject

- (BOOL)isEqual:(id)object;
- (NSUInteger)hash;

- (Class)superclass;
- (Class)class;
- (id)self;
- (struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;

- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;

- (BOOL)isProxy;

- (BOOL)isKindOfClass:(Class)aClass;
- (BOOL)isMemberOfClass:(Class)aClass;
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;

- (BOOL)respondsToSelector:(SEL)aSelector;

- (id)retain OBJC_ARC_UNAVAILABLE;
- (oneway void)release OBJC_ARC_UNAVAILABLE;
- (id)autorelease OBJC_ARC_UNAVAILABLE;
- (NSUInteger)retainCount OBJC_ARC_UNAVAILABLE;

- (NSString *)description;
@optional
- (NSString *)debugDescription;

@end


__OSX_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0)
OBJC_ROOT_CLASS
OBJC_EXPORT
@interface NSObject  {
    Class isa  OBJC_ISA_AVAILABILITY;
}

+ (void)load;

+ (void)initialize;
- (id)init;

+ (id)new;
+ (id)allocWithZone:(struct _NSZone *)zone;
+ (id)alloc;
- (void)dealloc;

- (void)finalize;

- (id)copy;
- (id)mutableCopy;

+ (id)copyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;
+ (id)mutableCopyWithZone:(struct _NSZone *)zone OBJC_ARC_UNAVAILABLE;

+ (Class)superclass;
+ (Class)class;
+ (BOOL)instancesRespondToSelector:(SEL)aSelector;
+ (BOOL)conformsToProtocol:(Protocol *)protocol;
- (IMP)methodForSelector:(SEL)aSelector;
+ (IMP)instanceMethodForSelector:(SEL)aSelector;
- (void)doesNotRecognizeSelector:(SEL)aSelector;

- (id)forwardingTargetForSelector:(SEL)aSelector __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
- (void)forwardInvocation:(NSInvocation *)anInvocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;

+ (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector;

- (BOOL)allowsWeakReference UNAVAILABLE_ATTRIBUTE;
- (BOOL)retainWeakReference UNAVAILABLE_ATTRIBUTE;

+ (NSString *)description;

+ (BOOL)isSubclassOfClass:(Class)aClass;

+ (BOOL)resolveClassMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);
+ (BOOL)resolveInstanceMethod:(SEL)sel __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

@end

#endif

#endif

关于getter与setting

1111

在getter里面里面调整getter时,要注意造成死循环.

-(NSURL *)imageURL
{
    if (!_imageURL) {
        NSURL *url = [[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:[NSString stringWithFormat:@"%.0f", [NSDate timeIntervalSinceReferenceDate]]];
        NSLog(@"url is %@",url);

        NSData *data = UIImageJPEGRepresentation(self.image, 1.0);
        if ([data writeToURL:url atomically:YES]) {
            _imageURL = url;
        }else{
            NSLog(@"There's no Image;");
        } NSLog(@"_imageURL is %@",_imageURL);
        NSLog(@"self.imageurl is %@",self.imageURL);

    }

    NSLog(@"2_imageURL is %@",_imageURL);

    return _imageURL;

}

 

Storyboard Unwind Segue有identifier

Unwind segue:

总的来说,需要两个normal segue.两个done to exit
第一个:-(IBAction)TakePhotosDone:(UIStoryboardSegue *)segue
第二个:通过通过unwind identifiery识别进行,normal segue

The Unwind segue’s name is : Unwind segue from Done to exit;

setting Storyboard Unwind Segue’s identifier at the …

unwind to exit时,通过unwind identifier创建object:
if ([segue.identifier isEqualToString:ADD_PHOTO]) {
NSManagedObjectContext *context = self.PhotographerOfTheUser.managedObjectContext;
Photo * photo = [NSEntityDescription insertNewObjectForEntityForName:@”Photo” inManagedObjectContext:context];

Handling the Events of a Map View

//
//  MyAnnotation.h
//  Maps
//
//  Created by Vandad Nahavandipoor on 11-05-10.
//  Copyright 2011  All rights reserved.
//

#import 
#import 

@interface MyAnnotation : NSObject 

@property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
@property (nonatomic, copy, readonly) NSString *title;
@property (nonatomic, copy, readonly) NSString *subtitle;

- (instancetype) initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
                               title:(NSString *)paramTitle
                            subTitle:(NSString *)paramSubTitle;

@end

.......
//
//  MyAnnotation.m
//  Maps
//
//  Created by Vandad Nahavandipoor on 11-05-10.
//  Copyright 2011  All rights reserved.
//

#import "MyAnnotation.h"

@implementation MyAnnotation

- (instancetype) initWithCoordinates:(CLLocationCoordinate2D)paramCoordinates
                               title:(NSString *)paramTitle
                            subTitle:(NSString *)paramSubTitle{
    
    self = [super init];
    
    if (self != nil){
        _coordinate = paramCoordinates;
        _title = paramTitle;
        _subtitle = paramSubTitle;
    }
    
    return self;
    
}

@end

//
//  ViewController.m
//  Handling the Events of a Map View
//
//  Created by Vandad NP on 24/06/2013.
//  Copyright (c) 2013 Pixolity Ltd. All rights reserved.
//

#import "ViewController.h"
#import 

@interface ViewController () 
@property (nonatomic, strong) MKMapView *myMapView;
@end

@implementation ViewController

- (void)viewDidLoad{
    [super viewDidLoad];
    
    /* Create a map as big as our view */
    self.myMapView = [[MKMapView alloc]
                      initWithFrame:self.view.bounds];
    
    /* Set the map type to Satellite */
    self.myMapView.mapType = MKMapTypeSatellite;
    
    self.myMapView.delegate = self;
    
    self.myMapView.autoresizingMask =
    UIViewAutoresizingFlexibleWidth |
    UIViewAutoresizingFlexibleHeight;
    
    /* Add it to our view */
    [self.view addSubview:self.myMapView];
    
}

@end

// Displaying Pins on a Map View

//
//  ViewController.m
//  Displaying Pins on a Map View
//
//  Created by Vandad NP on 24/06/2013.
//  Copyright (c) 2013 Pixolity Ltd. All rights reserved.
//

#import "ViewController.h"
#import "MyAnnotation.h"
#import 

@interface ViewController () 
@property (nonatomic, strong) MKMapView *myMapView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /* Create a map as big as our view */
    self.myMapView = [[MKMapView alloc]
                      initWithFrame:self.view.bounds];
    
    self.myMapView.delegate = self;
    
    /* Set the map type to Standard */
    self.myMapView.mapType = MKMapTypeStandard;
    
    self.myMapView.autoresizingMask =
        UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight;
    
    /* Add it to our view */
    [self.view addSubview:self.myMapView];
    
    /* This is just a sample location */
    CLLocationCoordinate2D location =
    CLLocationCoordinate2DMake(50.82191692907181, -0.13811767101287842);
    
    /* Create the annotation using the location */
    MyAnnotation *annotation =
    [[MyAnnotation alloc] initWithCoordinates:location
                                        title:@"My Title"
                                     subTitle:@"My Sub Title"];
    
    /* And eventually add it to the map */
    [self.myMapView addAnnotation:annotation];
    
}

@end

//
//  PhotosByPhotographerMapViewController.m
//  Photomania
//
//  Created by xushao on 13-12-26.
//  Copyright (c) 2013年 Stanford University. All rights reserved.
//

#import "PhotosByPhotographerMapViewController.h"
#import 
#import "Photo+Annotation.h"
#import "FlickrFetcher.h"
#import "ImageViewController.h"
#import "Photographer+Create.h"
#import "AddPhotoViewController.h"

@interface PhotosByPhotographerMapViewController ()
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (nonatomic, strong)UIImageView *imageViews;
@property (nonatomic, strong )NSArray *photosByPhotographer; // of photots

@property (weak, nonatomic) IBOutlet UIBarButtonItem *addPhotoBarButtonItem;
@property(nonatomic,strong)ImageViewController *imageViewController;

@end

@implementation PhotosByPhotographerMapViewController



-(MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation
{
   static NSString *identifier =  @"PhotosByPhotographerMapViewController";
    MKAnnotationView *view = [mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
    if (!view) {
        view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
        view.canShowCallout = YES;
        if (!self.imageViewController) {
            self.imageViews = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 46, 46)];
            view.leftCalloutAccessoryView = self.imageViews;
            UIButton *button = [[UIButton alloc] init];
            [button setBackgroundImage:[UIImage imageNamed:@"disclosure"] forState:UIControlStateNormal];
            [button sizeToFit];
            view.rightCalloutAccessoryView = button;
        }
        
    }
    
    return view;
}

-(void)updateLeftCalloutaccessoryViewInAnnotationView:(MKAnnotationView *)annotationView{
    if ([annotationView.leftCalloutAccessoryView isKindOfClass:[UIImageView class]]) {
        self.imageViews = (UIImageView *)annotationView.leftCalloutAccessoryView;
        if (self.imageViews) {
            if ([annotationView.annotation isKindOfClass:[Photo class]]) {
                Photo *photo = (Photo *)annotationView.annotation;
                self.imageViews.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:photo.thumbnail]]];
        }
          
        }
    }
}

-(void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control{
 

}

-(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
  
    if (self.imageViewController) {
        [self prepareViewController:self.imageViewController
                withSegueIdentifier:nil
                   toShowAnnotation:view.annotation ];
    }else{
        [self performSegueWithIdentifier:@"Show Photo" sender:view];
    }
    

}

-(void)updateMapAnnotation{
    
    [self.mapView removeAnnotations:self.mapView.annotations];
    [self.mapView addAnnotations:self.photosByPhotographer];
    [self.mapView showAnnotations:self.photosByPhotographer animated:YES];
    if (self.imageViewController) {
        Photo *autoPhot = [self.photosByPhotographer firstObject];
        
        [self prepareViewController:self.imageViewController withSegueIdentifier:nil toShowAnnotation:autoPhot];
        
    }
}

-(void)setMapView:(MKMapView *)mapView
{
    _mapView = mapView;
    
    self.mapView.delegate = self;
    self.title = self.photographer.name;
    [self updateMapAnnotation];
    
}

-(void)setPhotographer:(Photographer *)photographer
{
    _photographer = photographer;
    self.photosByPhotographer = nil;
    [self updateMapAnnotation];
    [self updateRightBarButtonItem];
    
    
}

-(void)updateRightBarButtonItem
{
    if (self.addPhotoBarButtonItem) {
        NSMutableArray *rightBarButtonItem = [self.navigationItem.rightBarButtonItems mutableCopy];
        if (!rightBarButtonItem)
            rightBarButtonItem = [[NSMutableArray alloc] init];
            NSUInteger rightButtonIndex = [rightBarButtonItem indexOfObject:self.addPhotoBarButtonItem];
            BOOL canAddPhotos = self.photographer.isUser;
            if (rightButtonIndex == NSNotFound) {
                if(canAddPhotos)[rightBarButtonItem addObject:self.addPhotoBarButtonItem];
            }else{
                if (!canAddPhotos) {
                    [rightBarButtonItem removeObjectAtIndex:rightButtonIndex];
                }
            }
            self.navigationItem.rightBarButtonItems = rightBarButtonItem;
        }
    
}
-(NSArray *)photosByPhotographer
{
    if (!_photosByPhotographer) {
        
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
        
        request.predicate = [NSPredicate predicateWithFormat:@"whoTook = %@", self.photographer];
        
        _photosByPhotographer = [self.photographer.managedObjectContext executeFetchRequest:request error:NULL];
    }
    
    return _photosByPhotographer;
}

-(ImageViewController *)imageViewController
{
    id detailsvc = [self.splitViewController.viewControllers lastObject];
    if ([detailsvc isKindOfClass:[UINavigationController class]]) {
         detailsvc = [((UINavigationController *)detailsvc).viewControllers firstObject];
        
    }
    
    return [detailsvc isKindOfClass:[ImageViewController class]] ? detailsvc : nil;
}


-(void)prepareViewController:(id)vc
         withSegueIdentifier:(NSString *)segueIdentifier
            toShowAnnotation:(id) annotation{
    
    Photo *photo = nil;
    if ([annotation isKindOfClass:[Photo class]]) {
        photo = (Photo *)annotation;
        
    }
    if (photo) {
        if (![segueIdentifier length] || [segueIdentifier isEqualToString:@"Show Photo"]) {
            if ([vc isKindOfClass:[ImageViewController class]]) {
                ImageViewController *dvc = (ImageViewController *)vc;
                dvc.imageURL = [NSURL URLWithString:photo.imageURL];
                dvc.title = photo.title;
            }
        }
    }
    
}

-(IBAction)TakePhotosDone:(UIStoryboardSegue *)segue
{

    if ([segue.sourceViewController isKindOfClass:[AddPhotoViewController class]]) {
        AddPhotoViewController *sourchVc = (AddPhotoViewController *)segue.sourceViewController;
        Photo *photo = sourchVc.addPhotos;
        if (photo) {
            [self.mapView addAnnotation:photo];
            [self.mapView showAnnotations:@[photo] animated:YES];
            self.photosByPhotographer = nil;
        }else
        {
            NSLog(@"User didn't take any photo");
        }
    }
}



-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{

    if ([segue.destinationViewController isKindOfClass:[AddPhotoViewController class]]) {
        AddPhotoViewController *apvc = (AddPhotoViewController *)segue.destinationViewController;
        apvc.PhotographerOfTheUser = self.photographer;
    }
    
    if ([sender isKindOfClass:[MKAnnotationView class]]) {
        MKAnnotationView *annotationView = (MKAnnotationView *)sender;
        
        [self prepareViewController:segue.destinationViewController withSegueIdentifier:segue.identifier toShowAnnotation:annotationView.annotation];
    }
}



@end

// Searching on a Map View

//
//  ViewController.m
//  Searching on a Map View
//
//  Created by Vandad NP on 24/06/2013.
//  Copyright (c) 2013 Pixolity Ltd. All rights reserved.
//

#import "ViewController.h"
#import 

@interface ViewController () 
@property (nonatomic, strong) MKMapView *myMapView;
@end

@implementation ViewController

- (void)                mapView:(MKMapView *)mapView
   didFailToLocateUserWithError:(NSError *)error{
    UIAlertView *alertView = [[UIAlertView alloc]
                              initWithTitle:@"Failed"
                              message:@"Could not get the user's location"
                              delegate:nil cancelButtonTitle:@"OK"
                              otherButtonTitles:nil];
    [alertView show];
}

- (void)        mapView:(MKMapView *)mapView
  didUpdateUserLocation:(MKUserLocation *)userLocation{
    
    MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
    request.naturalLanguageQuery = @"restaurants";
    
    MKCoordinateSpan span = MKCoordinateSpanMake(0.01, 0.01);
    
    request.region =
    MKCoordinateRegionMake(userLocation.location.coordinate, span);
    
    MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
    
    [search startWithCompletionHandler:
     ^(MKLocalSearchResponse *response, NSError *error) {
        
         for (MKMapItem *item in response.mapItems){
             
             NSLog(@"Item name = %@", item.name);
             NSLog(@"Item phone number = %@", item.phoneNumber);
             NSLog(@"Item url = %@", item.url);
             NSLog(@"Item location = %@", item.placemark.location);
             
         }
         
    }];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    /* Create a map as big as our view */
    self.myMapView = [[MKMapView alloc]
                      initWithFrame:self.view.bounds];
    
    self.myMapView.delegate = self;
    
    /* Set the map type to Standard */
    self.myMapView.mapType = MKMapTypeStandard;
    
    self.myMapView.autoresizingMask =
        UIViewAutoresizingFlexibleWidth |
        UIViewAutoresizingFlexibleHeight;
    
    self.myMapView.showsUserLocation = YES;
    self.myMapView.userTrackingMode = MKUserTrackingModeFollow;
    
    /* Add it to our view */
    [self.view addSubview:self.myMapView];
    
}

@end

// Displaying Directions on The Map

//
//  AppDelegate.m
//  Displaying Directions on The Map
//
//  Created by Vandad NP on 03/07/2013.
//  Copyright (c) 2013 Pixolity Ltd. All rights reserved.
//

#import "AppDelegate.h"
#import 
#import 

@implementation AppDelegate

- (BOOL)            application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

    NSString *destination = @"Churchill Square Shopping Center, \
    Brighton, United Kingdom";
    
    [[CLGeocoder new]
     geocodeAddressString:destination
     completionHandler:^(NSArray *placemarks, NSError *error) {
         
         if (error != nil){
             /* Handle the error here perhaps by displaying an alert */
             return;
         }
         
         MKDirectionsRequest *request = [[MKDirectionsRequest alloc] init];
         request.source = [MKMapItem mapItemForCurrentLocation];
         
         /* Convert the CoreLocation destination 
          placemark to a MapKit placemark */
         /* Get the placemark of the destination address */
         CLPlacemark *placemark = placemarks[0];
         CLLocationCoordinate2D destinationCoordinates =
         placemark.location.coordinate;
         MKPlacemark *destination = [[MKPlacemark alloc]
                                     initWithCoordinate:destinationCoordinates
                                     addressDictionary:nil];
         
         request.destination = [[MKMapItem alloc]
                                initWithPlacemark:destination];
         
         /* Set the transportation method to automobile */
         request.transportType = MKDirectionsTransportTypeAutomobile;
         
         /* Get the directions */
         MKDirections *directions = [[MKDirections alloc]
                                     initWithRequest:request];
         [directions calculateDirectionsWithCompletionHandler:
          ^(MKDirectionsResponse *response, NSError *error) {
              
              /* You can manually parse the response but in here we will take
               a shortcut and use the Maps app to display our source and
               destination. We didn't have to make this API call at all
               as we already had the map items before but this is to
               demonstrate that the directions response contains more
               information than just the source and the destination */
              
              /* Display the directions on the Maps app */
              [MKMapItem
               openMapsWithItems:@[response.source, response.destination]
               launchOptions:@{
                               MKLaunchOptionsDirectionsModeKey :
                                   MKLaunchOptionsDirectionsModeDriving}];
          }];
         
     }];
    
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}


@end

// Converting Meaningful Addresses to Longitude and Latitude

//
//  ViewController.m
//  Converting Meaningful Addresses to Longitude and Latitude
//
//  Created by Vandad NP on 24/06/2013.
//  Copyright (c) 2013 Pixolity Ltd. All rights reserved.
//

#import "ViewController.h"
#import 

@interface ViewController ()
@property (nonatomic, strong) CLGeocoder *myGeocoder;
@end

@implementation ViewController

- (void)viewDidLoad{
    [super viewDidLoad];
    
    
    /* We have our address */
    NSString *oreillyAddress =
        @"1005 Gravenstein Highway North, Sebastopol, CA 95472, USA";
    
    self.myGeocoder = [[CLGeocoder alloc] init];
    
    [self.myGeocoder
     geocodeAddressString:oreillyAddress
     completionHandler:^(NSArray *placemarks, NSError *error) {
         
         if (placemarks.count > 0 && error == nil){
             
             NSLog(@"Found %lu placemark(s).",
                   (unsigned long)[placemarks count]);
             
             CLPlacemark *firstPlacemark = placemarks[0];
             
             NSLog(@"Longitude = %f",
                   firstPlacemark.location.coordinate.longitude);
             NSLog(@"Latitude = %f",
                   firstPlacemark.location.coordinate.latitude);
         }
         else if (placemarks.count == 0 &&
                  error == nil){
             NSLog(@"Found no placemarks.");
         }
         else if (error != nil){
             NSLog(@"An error occurred = %@", error);
         }
         
     }];
    
}

@end

// Converting Longitude and Latitude to a Meaningful Address

//
//  ViewController.m
//  Converting Longitude and Latitude to a Meaningful Address
//
//  Created by Vandad NP on 24/06/2013.
//  Copyright (c) 2013 Pixolity Ltd. All rights reserved.
//

#import "ViewController.h"
#import 
#import 

@interface ViewController ()
@property (nonatomic, strong) CLGeocoder *myGeocoder;
@end

@implementation ViewController

- (void)viewDidLoad{
    [super viewDidLoad];
    
    CLLocation *location = [[CLLocation alloc]
                            initWithLatitude:+38.4112810
                            longitude:-122.8409780f];
    
    self.myGeocoder = [[CLGeocoder alloc] init];
    
    [self.myGeocoder
     reverseGeocodeLocation:location
     completionHandler:^(NSArray *placemarks, NSError *error) {
         
         if (error == nil && placemarks.count > 0){
             CLPlacemark *placemark = placemarks[0];
             /* We received the results */
             NSLog(@"Country = %@", placemark.country);
             NSLog(@"Postal Code = %@", placemark.postalCode);
             NSLog(@"Locality = %@", placemark.locality);
         }
         else if (error == nil && placemarks.count == 0){
             NSLog(@"No results were returned.");
         }
         else if (error != nil){
             NSLog(@"An error occurred = %@", error);
         }
         
     }];
    
    
}

@end

cs193作业5: Top Places

//
//  FlickrHelper.h
//  TopPlaces
//
//  Created by xushao on 1/3/14.
//  Copyright (c) 2014 TGG. All rights reserved.
//

#import "FlickrFetcher.h"

@interface FlickrHelper : FlickrFetcher



+(void)loadTopPlaces:(void  (^)(NSArray *places, NSError *error))complitionhandler;


+(NSString *)titleOfPlace:(NSDictionary *)place;
+(NSString *)subtitleOfPlace:(NSDictionary *)place;
+(NSString *)countryOfPlace:(NSDictionary *)place;

+(NSArray *)sortedPlaces:(NSArray *)places;
+(NSDictionary *)placesByCountries:(NSArray *)places;
+(NSArray *)countriesFromPlacesByCountries:(NSDictionary *)placesByCountries;

@end

//
//  FlickrHelper.m
//  TopPlaces
//
//  Created by xushao on 1/3/14.
//  Copyright (c) 2014 TGG. All rights reserved.
//

#import "FlickrHelper.h"

@implementation FlickrHelper


#define FLICKR_RESULTS_PLACES @"places.place"
#define FLICKR_PLACE_NAME @"_content"

+(void)loadTopPlaces:(void  (^)(NSArray *places, NSError *error))complitionhandler
{
    
    NSURLSessionConfiguration *confir = [ NSURLSessionConfiguration ephemeralSessionConfiguration];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:confir];
    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:[FlickrFetcher URLforTopPlaces] completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSDictionary *propertyList = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL:location] options:0 error:NULL];
            NSArray *places = [propertyList valueForKeyPath:FLICKR_RESULTS_PLACES];
            complitionhandler(places, error);
            
        });
    }];
    
    [task resume];
    
}

+(NSString *)titleOfPlace:(NSDictionary *)place
{
    NSString *placeParts =  [place valueForKeyPath:FLICKR_PLACE_NAME];
    
    return [[placeParts componentsSeparatedByString:@", "] firstObject];
}
+(NSString *)subtitleOfPlace:(NSDictionary *)place
{
    NSString *placeParts =  [place valueForKeyPath:FLICKR_PLACE_NAME];
    NSRange range;
    range.location = 1;
    NSArray *partsArray = [placeParts componentsSeparatedByString:@", "];
    range.length = [partsArray count] - 2;
    
    return [[partsArray subarrayWithRange:range] componentsJoinedByString:@", "];
}
+(NSString *)countryOfPlace:(NSDictionary *)place
{
    NSString *placeParts =  [place valueForKeyPath:FLICKR_PLACE_NAME];

    return [[placeParts componentsSeparatedByString:@", "] lastObject];
}

+(NSArray *)sortedPlaces:(NSArray *)places
{
   return [places sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
        NSString *name1 = [obj1 valueForKeyPath:FLICKR_PLACE_NAME];
        NSString *name2 = [ obj2 valueForKeyPath:FLICKR_PLACE_NAME];
        return [name1 localizedCompare:name2];
    }];
}

+(NSDictionary *)placesByCountries:(NSArray *)places
{
    NSMutableDictionary *placesByCountries = [NSMutableDictionary dictionary];
    for (NSDictionary *place in places) {
        NSString *country = [FlickrHelper countryOfPlace:place];
        NSMutableArray *placesOfCountry = [placesByCountries objectForKey:country];
        if (!placesOfCountry) {
            placesOfCountry = [NSMutableArray array];
            placesByCountries[country]= placesOfCountry;
        }
        [placesOfCountry addObject:place];
    }
    return placesByCountries;
}

+(NSArray *)countriesFromPlacesByCountries:(NSDictionary *)placesByCountries
{
    NSArray *countries = [placesByCountries allKeys];
    
    countries = [countries sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
       return [obj1 compare:obj2 options:NSCaseInsensitiveSearch];
    }];
    return countries;
    
}








@end

//
//  TopPlacesTVC.h
//  TopPlaces
//
//  Created by xushao on 1/3/14.
//  Copyright (c) 2014 TGG. All rights reserved.
//

#import 

@interface TopPlacesTVC : UITableViewController

@property (nonatomic, strong)NSArray *places;
@end


.....
//
//  TopPlacesTVC.m
//  TopPlaces
//
//  Created by xushao on 1/3/14.
//  Copyright (c) 2014 TGG. All rights reserved.
//

#import "TopPlacesTVC.h"
#import "FlickrHelper.h"
#import "FlickrFetcher.h"

@interface TopPlacesTVC ()
@property(nonatomic,strong)NSArray *countries;
@property(nonatomic, strong)NSDictionary *placesByCountry;

@end

@implementation TopPlacesTVC



- (IBAction)fetchPlaces {
    
    [self.refreshControl beginRefreshing];
    [FlickrHelper loadTopPlaces:^(NSArray *places, NSError *error) {
        if (!error) {
            self.places = places;
            [self.refreshControl endRefreshing];
        }else{
            NSLog(@"error");
        }
    }];
    
}

-(void)viewDidLoad
{
    
    [super viewDidLoad];
    [self fetchPlaces];
}
-(void)setPlaces:(NSArray *)places
{
    _places = places;
    _places = [FlickrHelper sortedPlaces:_places];
    self.placesByCountry = [FlickrHelper placesByCountries:_places];
    self.countries = [FlickrHelper countriesFromPlacesByCountries:self.placesByCountry];
    
    [self.tableView reloadData];
}


#pragma mark - Table view data source

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    // Return the number of sections.
    return [self.countries count];
}

-(NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return self.countries[section];
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // Return the number of rows in the section.
    return [self.placesByCountry[self.countries[section]] count];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Top Places Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
    
    // Configure the cell...
    NSDictionary *place = self.placesByCountry[self.countries[indexPath.section]][indexPath.row];
    cell.textLabel.text = [FlickrHelper titleOfPlace:place];
    cell.detailTextLabel.text = [FlickrHelper subtitleOfPlace:place];
    
    return cell;
}

高级查询Key-Value Coding Programming Guide

22

 

@avg

The @avg operator uses valueForKeyPath: to get the values specified by the property specified by the key path to the right of the operator, converts each to a double, and returns the average value as an instance of NSNumber. In the case where the value is nil, 0 is assumed instead.

The following example returns the average value of the transaction amount for the objects in transactions:

NSNumber *transactionAverage = [transactions valueForKeyPath:@"@avg.amount"];

The formatted result of transactionAverage is $456.54.

@count

The @count operator returns the number of objects in the left key path collection as an instance of NSNumber, the key path to the right of the operator is ignored.

The following example returns the number of Transaction objects in transactions:

NSNumber *numberOfTransactions = [transactions valueForKeyPath:@"@count"];

The value of numberOfTransactions is 13.

@max

The @max operator compares the values of the property specified by the key path to the right of the operator and returns the maximum value found. The maximum value is determined using the compare: method of the objects at the specified key path. The compared property objects must support comparison with each other. If the value of the right side of the key path is nil, it is ignored.

The following example returns the maximum value of the date values (date of the latest transaction) for the Transaction objects in transactions:

NSDate *latestDate = [transactions valueForKeyPath:@"@max.date"];

The latestDate value (formatted) is Jul 15, 2010.

@min

The @min operator compares the values of the property specified by the key path to the right of the operator and returns the minimum value found. The minimum value is determined using the compare: method of the objects at the specified key path. The compared property objects must support comparison with each other. If the value of the right side of the key path is nil, it is ignored.

The following example returns the minimum value (date of the earliest transaction) of the date property for the Transaction objects in transactions:

NSDate *earliestDate = [transactions valueForKeyPath:@"@min.date"];

The earliestDate value (formatted) is Dec 1, 2009.

@sum

The @sum operator returns the sum of the values of the property specified by the key path to the right of the operator. Each number is converted to a double, the sum of the values is computed, and the total is wrapped as an instance of NSNumber and returned. If the value of the right side of the key path is nil, it is ignored.

The following example returns the sum of the amounts property for the transactions in transactions:

NSNumber *amountSum = [transactions valueForKeyPath:@"@sum.amount"];

The resulting amountSum value (formatted) is $5,935.00

Object Operators

The object operators provide results when they are applied to a single collection instance.

@distinctUnionOfObjects

The @distinctUnionOfObjects operator returns an array containing the distinct objects in the property specified by the key path to the right of the operator.

The following example returns the payee property values for the transactions in transactions with any duplicate values removed:

NSArray *payees = [transactions valueForKeyPath:@"@distinctUnionOfObjects.payee"];

The resulting payees array contains the following strings: Car Loan, General Cable, Animal Hospital, Green Power, Mortgage.

The @unionOfObjects operator is similar, but does not remove duplicate objects.

@unionOfObjects

The @unionOfObjects operator returns an array containing the distinct objects in the property specified by the key path to the right of the operator. Unlike“@distinctUnionOfObjects,” duplicate objects are not removed.

The following example returns the payee property values for the transactions in transactions:

NSArray *payees = [transactions valueForKeyPath:@"@unionOfObjects.payee"];

The resulting payees array contains the following strings: Green Power, Green Power, Green Power, Car Loan, Car Loan, Car Loan, General Cable, General Cable, General Cable, Mortgage, Mortgage, Mortgage, Animal Hospital.

The @distinctUnionOfArrays operator is similar, but removes duplicate objects.

MapView

– (void)mapView:(MKMapView *)sender didSelectAnnotationView:(MKAnnotationView *)aView;

- (MKAnnotationView *)mapView:(MKMapView *)sender
viewForAnnotation:(id )annotation
{
MKAnnotationView *aView = [sender dequeueReusableAnnotationViewWithIdentifier:IDENT];
if (!aView) {
aView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                                reuseIdentifier:IDENT];
// set canShowCallout to YES and build aView’s callout accessory views here }
aView.annotation = annotation; // yes, this happens twice if no dequeue
// maybe load up accessory views here (if not too expensive)?
// or reset them and wait until mapView:didSelectAnnotationView: to load actual data
return aView; }
MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init]; request.naturalLanguageQuery = @“Ike’s”;
request.region = ...; // e.g., Stanford campus
MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
[search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
// response contains an array of MKMapItem which contains MKPlacemark }];
//
//  PhotosByPhotographerMapViewController.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "PhotosByPhotographerMapViewController.h"
#import 
#import "Photo+Annotation.h"
#import "ImageViewController.h"

@interface PhotosByPhotographerMapViewController () 
@property (weak, nonatomic) IBOutlet MKMapView *mapView;
@property (nonatomic, strong) NSArray *photosByPhotographer; // of Photo
@property (nonatomic, strong) ImageViewController *imageViewController; // can be nil
@end

@implementation PhotosByPhotographerMapViewController

#pragma mark - Properties

// lazily fetch the photos by our photographer from Core Data

- (NSArray *)photosByPhotographer
{
    if (!_photosByPhotographer) {
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
        request.predicate = [NSPredicate predicateWithFormat:@"whoTook = %@", self.photographer];
        _photosByPhotographer = [self.photographer.managedObjectContext executeFetchRequest:request
                                                                                      error:NULL];
    }
    return _photosByPhotographer;
}

// when our photographer changes, clear out our photosByPhotographer
// and update the map (if self.mapView is even set at this point)

- (void)setPhotographer:(Photographer *)photographer
{
    _photographer = photographer;
    self.title = photographer.name;
    self.photosByPhotographer = nil;
    [self updateMapViewAnnotations];
}

// when the mapView outlet gets set, set its delegate to ourself
// also, update the annotations to be our photosByPhotographer (if set yet)

- (void)setMapView:(MKMapView *)mapView
{
    _mapView = mapView;
    self.mapView.delegate = self;
    [self updateMapViewAnnotations];
}

// remove all existing annotations from the map
// and add all of our photosByPhotographer to the map
// zoom the map to show them all
// if we are capable of showing a photo immediately
//   (i.e. self.imageViewController is not nil)
//   then pick one of the photos at random and show it

- (void)updateMapViewAnnotations
{
    [self.mapView removeAnnotations:self.mapView.annotations];
    [self.mapView addAnnotations:self.photosByPhotographer];
    [self.mapView showAnnotations:self.photosByPhotographer animated:YES];
    if (self.imageViewController) {
        Photo *autoselectedPhoto = [self.photosByPhotographer firstObject];
        if (autoselectedPhoto) {
            [self.mapView selectAnnotation:autoselectedPhoto animated:YES];
            [self prepareViewController:self.imageViewController
                               forSegue:nil
                       toShowAnnotation:autoselectedPhoto];
        }
    }
}

// see if we can find an ImageViewController to show the selected annotation's image
// currently we look at the detail of a split view we are in (if any)

- (ImageViewController *)imageViewController
{
    id detailvc = [self.splitViewController.viewControllers lastObject];
    if ([detailvc isKindOfClass:[UINavigationController class]]) {
        detailvc = [((UINavigationController *)detailvc).viewControllers firstObject];
    }
    return [detailvc isKindOfClass:[ImageViewController class]] ? detailvc : nil;
}

#pragma mark - MKMapViewDelegate

// enhances our callout to have left (UIImageView) and right (UIButton) accessory views
// only does this if we are going to need to segue to a different VC to show a photo
//  (because, if not (i.e. self.imageViewController is not nil), the photo will already be on screen
//   so there is no reason to show its thumbnail or make the user click again on disclosure button)

- (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id)annotation
{
    static NSString *reuseId = @"PhotosByPhotographerMapViewController";
    MKAnnotationView *view = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseId];
    if (!view) {
        view = [[MKPinAnnotationView alloc] initWithAnnotation:annotation
                                               reuseIdentifier:reuseId];
        view.canShowCallout = YES;
        if (!self.imageViewController) {
            UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 46, 46)];
            view.leftCalloutAccessoryView = imageView;
            UIButton *disclosureButton = [[UIButton alloc] init];
            [disclosureButton setBackgroundImage:[UIImage imageNamed:@"disclosure"] forState:UIControlStateNormal];
            [disclosureButton sizeToFit];
            view.rightCalloutAccessoryView = disclosureButton;
        }
    }
    
    view.annotation = annotation;
    
    return view;
}

// called when the MKAnnotationView (the pin) is clicked on
// either updates the left callout accessory (UIImageView)
// or shows the Photo annotation in self.imageViewController (if available)

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
{
    if (self.imageViewController) {
        [self prepareViewController:self.imageViewController
                           forSegue:nil
                   toShowAnnotation:view.annotation];
    } else {
        [self updateLeftCalloutAccessoryViewInAnnotationView:view];
    }
}

// checks to be sure that the annotationView's left callout is a UIImageView
// if it is and if the annotation is a Photo, then shows the thumbnail
// this should do that fetch in another thread
// but when the thumbnail image came back, it would need to double check the annotationView
// to be sure it is still displaying the annotation for which we fetched
// (because MKAnnotationViews, like UITableViewCells, are reused)

- (void)updateLeftCalloutAccessoryViewInAnnotationView:(MKAnnotationView *)annotationView
{
    UIImageView *imageView = nil;
    if ([annotationView.leftCalloutAccessoryView isKindOfClass:[UIImageView class]]) {
        imageView = (UIImageView *)annotationView.leftCalloutAccessoryView;
    }
    if (imageView) {
        Photo *photo = nil;
        if ([annotationView.annotation isKindOfClass:[Photo class]]) {
            photo = (Photo *)annotationView.annotation;
        }
        if (photo) {
#warning Blocking main queue!
            imageView.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:photo.thumbnailURL]]];
        }
    }
}

// called when the right callout accessory view is tapped
// (it is the only accessory view we have that inherits from UIControl)
// will crash the program if this View Controller does not have a @"Show Photo" segue
// in the storyboard

- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
{
    [self performSegueWithIdentifier:@"Show Photo" sender:view];
}

#pragma mark - Navigation

// if the annotation is a Photo, this passes its imageURL to vc (if vc is an ImageViewController)

- (void)prepareViewController:(id)vc
                     forSegue:(NSString *)segueIdentifier
             toShowAnnotation:(id )annotation
{
    Photo *photo = nil;
    if ([annotation isKindOfClass:[Photo class]]) {
        photo = (Photo *)annotation;
    }
    if (photo) {
        if (![segueIdentifier length] || [segueIdentifier isEqualToString:@"Show Photo"]) {
            if ([vc isKindOfClass:[ImageViewController class]]) {
                ImageViewController *ivc = (ImageViewController *)vc;
                ivc.imageURL = [NSURL URLWithString:photo.imageURL];
                ivc.title = photo.title;
            }
        }
    }
}

// if sender is an MKAnnotationView, this calls prepareViewController:forSegue:toShowAnnotation:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([sender isKindOfClass:[MKAnnotationView class]]) {
        [self prepareViewController:segue.destinationViewController
                           forSegue:segue.identifier
                   toShowAnnotation:((MKAnnotationView *)sender).annotation];
    }
}

@end

Photomania Universal URL

ImageViewController.m

//
//  ImageViewController.m
//  Imaginarium
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "ImageViewController.h"
#import "URLViewController.h"

@interface ImageViewController () 
@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, strong) UIImage *image;
@property (weak, nonatomic) IBOutlet UIScrollView *scrollView;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *spinner;
// this is weak because we want it to go back to nil
//   when no one else has strong pointer to the popover (i.e. it is dismissed)
@property (weak, nonatomic) UIPopoverController *urlPopoverController;
@end

@implementation ImageViewController

#pragma mark - View Controller Lifecycle

// add the UIImageView to the MVC's View

- (void)viewDidLoad
{
    [super viewDidLoad];
    [self.scrollView addSubview:self.imageView];
}

#pragma mark - Properties

// lazy instantiation

- (UIImageView *)imageView
{
    if (!_imageView) _imageView = [[UIImageView alloc] init];
    return _imageView;
}

// image property does not use an _image instance variable
// instead it just reports/sets the image in the imageView property
// thus we don't need @synthesize even though we implement both setter and getter

- (UIImage *)image
{
    return self.imageView.image;
}

- (void)setImage:(UIImage *)image
{
    self.imageView.image = image; // does not change the frame of the UIImageView

    // had to add these two lines in Shutterbug to fix a bug in "reusing" ImageViewController's MVC
    self.scrollView.zoomScale = 1.0;
    self.imageView.frame = CGRectMake(0, 0, image.size.width, image.size.height);
    
    // self.scrollView could be nil on the next line if outlet-setting has not happened yet
    self.scrollView.contentSize = self.image ? self.image.size : CGSizeZero;

    // in portrait orientation on an iPad in a split view,
    //   unfortunately the master can be access while popover is up
    //   (so dismiss the URL if someone changes our image from there)
    [self.urlPopoverController dismissPopoverAnimated:YES];

    [self.spinner stopAnimating];
}

- (void)setScrollView:(UIScrollView *)scrollView
{
    _scrollView = scrollView;
    
    // next three lines are necessary for zooming
    _scrollView.minimumZoomScale = 0.2;
    _scrollView.maximumZoomScale = 2.0;
    _scrollView.delegate = self;

    // next line is necessary in case self.image gets set before self.scrollView does
    // for example, prepareForSegue:sender: is called before outlet-setting phase
    self.scrollView.contentSize = self.image ? self.image.size : CGSizeZero;
}

#pragma mark - UIScrollViewDelegate

// mandatory zooming method in UIScrollViewDelegate protocol

- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
    return self.imageView;
}

#pragma mark - Navigation

// show our imageURL in a popover
// stash the popover so that we can ensure that only one appears at a time

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.destinationViewController isKindOfClass:[URLViewController class]]) {
        URLViewController *urlvc = (URLViewController *)segue.destinationViewController;
        // if we are segueing to a popover, the segue itself will be a UIStoryboardPopoverSegue
        if ([segue isKindOfClass:[UIStoryboardPopoverSegue class]]) {
            UIStoryboardPopoverSegue *popoverSegue = (UIStoryboardPopoverSegue *)segue;
            self.urlPopoverController = popoverSegue.popoverController;
        }
        urlvc.url = self.imageURL;
    }
}

// don't show the URL if it's already showing or we don't have a URL to show

- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
{
    if ([identifier isEqualToString:@"Show URL"]) {
        return self.urlPopoverController ? NO : (self.imageURL ? YES : NO);
    } else {
        return [super shouldPerformSegueWithIdentifier:identifier sender:sender];
    }
}

#pragma mark - Setting the Image from the Image's URL

- (void)setImageURL:(NSURL *)imageURL
{
    _imageURL = imageURL;
    //    self.image = [UIImage imageWithData:[NSData dataWithContentsOfURL:self.imageURL]]; // blocks main queue!
    [self startDownloadingImage];
}

- (void)startDownloadingImage
{
    self.image = nil;

    if (self.imageURL)
    {
        [self.spinner startAnimating];

        NSURLRequest *request = [NSURLRequest requestWithURL:self.imageURL];
        
        // another configuration option is backgroundSessionConfiguration (multitasking API required though)
        NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        
        // create the session without specifying a queue to run completion handler on (thus, not main queue)
        // we also don't specify a delegate (since completion handler is all we need)
        NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];

        NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request
            completionHandler:^(NSURL *localfile, NSURLResponse *response, NSError *error) {
                // this handler is not executing on the main queue, so we can't do UI directly here
                if (!error) {
                    if ([request.URL isEqual:self.imageURL]) {
                        // UIImage is an exception to the "can't do UI here"
                        UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:localfile]];
                        // but calling "self.image =" is definitely not an exception to that!
                        // so we must dispatch this back to the main queue
                        dispatch_async(dispatch_get_main_queue(), ^{ self.image = image; });
                    }
                }
        }];
        [task resume]; // don't forget that all NSURLSession tasks start out suspended!
    }
}

#pragma mark - UISplitViewControllerDelegate

// this section added during Shutterbug demo

- (void)awakeFromNib
{
    self.splitViewController.delegate = self;
}

- (BOOL)splitViewController:(UISplitViewController *)svc
   shouldHideViewController:(UIViewController *)vc
              inOrientation:(UIInterfaceOrientation)orientation
{
    return UIInterfaceOrientationIsPortrait(orientation);
}

- (void)splitViewController:(UISplitViewController *)svc
     willHideViewController:(UIViewController *)aViewController
          withBarButtonItem:(UIBarButtonItem *)barButtonItem
       forPopoverController:(UIPopoverController *)pc
{
    barButtonItem.title = aViewController.title;
    self.navigationItem.leftBarButtonItem = barButtonItem;
}

- (void)splitViewController:(UISplitViewController *)svc
     willShowViewController:(UIViewController *)aViewController
  invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem
{
    self.navigationItem.leftBarButtonItem = nil;
}

@end

Photo+Flickr

//
//  Photo+Flickr.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "Photo+Flickr.h"
#import "FlickrFetcher.h"
#import "Photographer+Create.h"

@implementation Photo (Flickr)

+ (Photo *)photoWithFlickrInfo:(NSDictionary *)photoDictionary
        inManagedObjectContext:(NSManagedObjectContext *)context
{
    Photo *photo = nil;
    
    NSString *unique = photoDictionary[FLICKR_PHOTO_ID];
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
    request.predicate = [NSPredicate predicateWithFormat:@"unique = %@", unique];
    
    NSError *error;
    NSArray *matches = [context executeFetchRequest:request error:&error];
    
    if (!matches || error || ([matches count] > 1)) {
        // handle error
    } else if ([matches count]) {
        photo = [matches firstObject];
    } else {
        photo = [NSEntityDescription insertNewObjectForEntityForName:@"Photo"
                                              inManagedObjectContext:context];
        photo.unique = unique;
        photo.title = [photoDictionary valueForKeyPath:FLICKR_PHOTO_TITLE];
        photo.subtitle = [photoDictionary valueForKeyPath:FLICKR_PHOTO_DESCRIPTION];
        photo.imageURL = [[FlickrFetcher URLforPhoto:photoDictionary format:FlickrPhotoFormatLarge] absoluteString];
        
        NSString *photographerName = [photoDictionary valueForKeyPath:FLICKR_PHOTO_OWNER];
        photo.whoTook = [Photographer photographerWithName:photographerName
                                    inManagedObjectContext:context];

    }

    return photo;
}

+ (void)loadPhotosFromFlickrArray:(NSArray *)photos // of Flickr NSDictionary
         intoManagedObjectContext:(NSManagedObjectContext *)context
{
    for (NSDictionary *photo in photos) {
        [self photoWithFlickrInfo:photo inManagedObjectContext:context];
    }
}

@end

Photographer+Create.m

//
//  Photographer+Create.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "Photographer+Create.h"

@implementation Photographer (Create)

+ (Photographer *)photographerWithName:(NSString *)name
                inManagedObjectContext:(NSManagedObjectContext *)context
{
    Photographer *photographer = nil;
    
    if ([name length]) {
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photographer"];
        request.predicate = [NSPredicate predicateWithFormat:@"name = %@", name];
        
        NSError *error;
        NSArray *matches = [context executeFetchRequest:request error:&error];
        
        if (!matches || ([matches count] > 1)) {
            // handle error
        } else if (![matches count]) {
            photographer = [NSEntityDescription insertNewObjectForEntityForName:@"Photographer"
                                                         inManagedObjectContext:context];
            photographer.name = name;
        } else {
            photographer = [matches lastObject];
        }
    }
    
    return photographer;
}

@end

PhotographersCDTVC.m

//
//  PhotographersCDTVC.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "PhotographersCDTVC.h"
#import "Photographer.h"
#import "PhotoDatabaseAvailability.h"
#import "PhotosByPhotographerCDTVC.h"

@implementation PhotographersCDTVC

- (void)awakeFromNib
{
    [[NSNotificationCenter defaultCenter] addObserverForName:PhotoDatabaseAvailabilityNotification
                                                      object:nil
                                                       queue:nil
                                                  usingBlock:^(NSNotification *note) {
                                                      self.managedObjectContext = note.userInfo[PhotoDatabaseAvailabilityContext];
                                                  }];
}

- (void)setManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
{
    _managedObjectContext = managedObjectContext;
    
    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photographer"];
    request.predicate = nil;
    request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name"
                                                              ascending:YES
                                                               selector:@selector(localizedStandardCompare:)]];

    
    
    self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                        managedObjectContext:managedObjectContext
                                                                          sectionNameKeyPath:nil
                                                                                   cacheName:nil];
}

#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Photographer Cell"];
    
    Photographer *photographer = [self.fetchedResultsController objectAtIndexPath:indexPath];
    
    cell.textLabel.text = photographer.name;
    cell.detailTextLabel.text = [NSString stringWithFormat:@"%d photos", (int)[photographer.photos count]];
    
    return cell;
}

#pragma mark - Navigation

- (void)prepareViewController:(id)vc forSegue:(NSString *)segueIdentifer fromIndexPath:(NSIndexPath *)indexPath
{
    Photographer *photographer = [self.fetchedResultsController objectAtIndexPath:indexPath];
    // note that we don't check the segue identifier here
    // probably fine ... hard to imagine any other way this class would segue to PhotosByPhotographerCDTVC
    if ([vc isKindOfClass:[PhotosByPhotographerCDTVC class]]) {
        PhotosByPhotographerCDTVC *pbpcdtvc = (PhotosByPhotographerCDTVC *)vc;
        pbpcdtvc.photographer = photographer;
    }
}

// boilerplate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSIndexPath *indexPath = nil;
    if ([sender isKindOfClass:[UITableViewCell class]]) {
        indexPath = [self.tableView indexPathForCell:sender];
    }
    [self prepareViewController:segue.destinationViewController
                       forSegue:segue.identifier
                  fromIndexPath:indexPath];
}

// boilerplate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    id detailvc = [self.splitViewController.viewControllers lastObject];
    if ([detailvc isKindOfClass:[UINavigationController class]]) {
        detailvc = [((UINavigationController *)detailvc).viewControllers firstObject];
        [self prepareViewController:detailvc
                           forSegue:nil
                      fromIndexPath:indexPath];
    }
}

@end

PhotomaniaAppDelegate.m

//
//  PhotomaniaAppDelegate.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "PhotomaniaAppDelegate.h"
#import "PhotomaniaAppDelegate+MOC.h"
#import "FlickrFetcher.h"
#import "Photo+Flickr.h"
#import "PhotoDatabaseAvailability.h"

// THIS FILE WANTS TO BE VERY WIDE BECAUSE IT HAS A LOT OF COMMENTS THAT ARE ATTACHED ONTO THE END OF LINES--MAKE THIS COMMENT FIT ON ONE LINE.
// (or turn off line wrapping)

@interface PhotomaniaAppDelegate() 
@property (copy, nonatomic) void (^flickrDownloadBackgroundURLSessionCompletionHandler)();
@property (strong, nonatomic) NSURLSession *flickrDownloadSession;
@property (strong, nonatomic) NSTimer *flickrForegroundFetchTimer;
@property (strong, nonatomic) NSManagedObjectContext *photoDatabaseContext;
@end

// name of the Flickr fetching background download session
#define FLICKR_FETCH @"Flickr Just Uploaded Fetch"

// how often (in seconds) we fetch new photos if we are in the foreground
#define FOREGROUND_FLICKR_FETCH_INTERVAL (20*60)

// how long we'll wait for a Flickr fetch to return when we're in the background
#define BACKGROUND_FLICKR_FETCH_TIMEOUT (10)

@implementation PhotomaniaAppDelegate

#pragma mark - UIApplicationDelegate

// this is called as soon as our storyboard is read in and we're ready to get started
// but it's still very early in the game (UI is not yet on screen, for example)

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // when we're in the background, fetch as often as possible (which won't be much)
    // forgot to include this line in the demo during lecture, but don't forget to include it in your app!
    [[UIApplication sharedApplication] setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];

    // we get our managed object context by creating it ourself in a category on PhotomaniaAppDelegate
    // but in your homework assignment, you must get your context from a UIManagedDocument
    // (i.e. you cannot use the method createMainQueueManagedObjectContext, or even use that approach)
    self.photoDatabaseContext = [self createMainQueueManagedObjectContext];
    
    // we fire off a Flickr fetch every time we launch (why not?)
    [self startFlickrFetch];
    
    // this return value has to do with handling URLs from other applications
    // don't worry about it for now, just return YES
    return YES;
}

// this is called occasionally by the system WHEN WE ARE NOT THE FOREGROUND APPLICATION
// in fact, it will LAUNCH US if necessary to call this method
// the system has lots of smarts about when to do this, but it is entirely opaque to us

- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
    // in lecture, we relied on our background flickrDownloadSession to do the fetch by calling [self startFlickrFetch]
    // that was easy to code up, but pretty weak in terms of how much it will actually fetch (maybe almost never)
    // that's because there's no guarantee that we'll be allowed to start that discretionary fetcher when we're in the background
    // so let's simply make a non-discretionary, non-background-session fetch here
    // we don't want it to take too long because the system will start to lose faith in us as a background fetcher and stop calling this as much
    // so we'll limit the fetch to BACKGROUND_FETCH_TIMEOUT seconds (also we won't use valuable cellular data)

    if (self.photoDatabaseContext) {
        NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration ephemeralSessionConfiguration];
        sessionConfig.allowsCellularAccess = NO;
        sessionConfig.timeoutIntervalForRequest = BACKGROUND_FLICKR_FETCH_TIMEOUT; // want to be a good background citizen!
        NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfig];
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:[FlickrFetcher URLforRecentGeoreferencedPhotos]];
        NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request
            completionHandler:^(NSURL *localFile, NSURLResponse *response, NSError *error) {
                if (error) {
                    NSLog(@"Flickr background fetch failed: %@", error.localizedDescription);
                    completionHandler(UIBackgroundFetchResultNoData);
                } else {
                    [self loadFlickrPhotosFromLocalURL:localFile
                                           intoContext:self.photoDatabaseContext
                                   andThenExecuteBlock:^{
                                       completionHandler(UIBackgroundFetchResultNewData);
                                   }
                     ];
                }
            }];
        [task resume];
    } else {
        completionHandler(UIBackgroundFetchResultNoData); // no app-switcher update if no database!
    }
}

// this is called whenever a URL we have requested with a background session returns and we are in the background
// it is essentially waking us up to handle it
// if we were in the foreground iOS would just call our delegate method and not bother with this

- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
{
    // this completionHandler, when called, will cause our UI to be re-cached in the app switcher
    // but we should not call this handler until we're done handling the URL whose results are now available
    // so we'll stash the completionHandler away in a property until we're ready to call it
    // (see flickrDownloadTasksMightBeComplete for when we actually call it)
    self.flickrDownloadBackgroundURLSessionCompletionHandler = completionHandler;
}

#pragma mark - Database Context

// we do some stuff when our Photo database's context becomes available
// we kick off our foreground NSTimer so that we are fetching every once in a while in the foreground
// we post a notification to let others know the context is available

- (void)setPhotoDatabaseContext:(NSManagedObjectContext *)photoDatabaseContext
{
    _photoDatabaseContext = photoDatabaseContext;
    
    // every time the context changes, we'll restart our timer
    // so kill (invalidate) the current one
    // (we didn't get to this line of code in lecture, sorry!)
    [self.flickrForegroundFetchTimer invalidate];
    self.flickrForegroundFetchTimer = nil;
    
    if (self.photoDatabaseContext)
    {
        // this timer will fire only when we are in the foreground
        self.flickrForegroundFetchTimer = [NSTimer scheduledTimerWithTimeInterval:FOREGROUND_FLICKR_FETCH_INTERVAL
                                         target:self
                                       selector:@selector(startFlickrFetch:)
                                       userInfo:nil
                                        repeats:YES];
    }
    
    // let everyone who might be interested know this context is available
    // this happens very early in the running of our application
    // it would make NO SENSE to listen to this radio station in a View Controller that was segued to, for example
    // (but that's okay because a segued-to View Controller would presumably be "prepared" by being given a context to work in)
    NSDictionary *userInfo = self.photoDatabaseContext ? @{ PhotoDatabaseAvailabilityContext : self.photoDatabaseContext } : nil;
    [[NSNotificationCenter defaultCenter] postNotificationName:PhotoDatabaseAvailabilityNotification
                                                        object:self
                                                      userInfo:userInfo];
}

#pragma mark - Flickr Fetching

// this will probably not work (task = nil) if we're in the background, but that's okay
// (we do our background fetching in performFetchWithCompletionHandler:)
// it will always work when we are the foreground (active) application

- (void)startFlickrFetch
{
    // getTasksWithCompletionHandler: is ASYNCHRONOUS
    // but that's okay because we're not expecting startFlickrFetch to do anything synchronously anyway
    [self.flickrDownloadSession getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
        // let's see if we're already working on a fetch ...
        if (![downloadTasks count]) {
            // ... not working on a fetch, let's start one up
            NSURLSessionDownloadTask *task = [self.flickrDownloadSession downloadTaskWithURL:[FlickrFetcher URLforRecentGeoreferencedPhotos]];
            task.taskDescription = FLICKR_FETCH;
            [task resume];
        } else {
            // ... we are working on a fetch (let's make sure it (they) is (are) running while we're here)
            for (NSURLSessionDownloadTask *task in downloadTasks) [task resume];
        }
    }];
}

- (void)startFlickrFetch:(NSTimer *)timer // NSTimer target/action always takes an NSTimer as an argument
{
    [self startFlickrFetch];
}

// the getter for the flickrDownloadSession @property

- (NSURLSession *)flickrDownloadSession // the NSURLSession we will use to fetch Flickr data in the background
{
    if (!_flickrDownloadSession) {
        static dispatch_once_t onceToken; // dispatch_once ensures that the block will only ever get executed once per application launch
        dispatch_once(&onceToken, ^{
            // notice the configuration here is "backgroundSessionConfiguration:"
            // that means that we will (eventually) get the results even if we are not the foreground application
            // even if our application crashed, it would get relaunched (eventually) to handle this URL's results!
            NSURLSessionConfiguration *urlSessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:FLICKR_FETCH];
            _flickrDownloadSession = [NSURLSession sessionWithConfiguration:urlSessionConfig
                                                                   delegate:self    // we MUST have a delegate for background configurations
                                                              delegateQueue:nil];   // nil means "a random, non-main-queue queue"
        });
    }
    return _flickrDownloadSession;
}

// standard "get photo information from Flickr URL" code

- (NSArray *)flickrPhotosAtURL:(NSURL *)url
{
    NSDictionary *flickrPropertyList;
    NSData *flickrJSONData = [NSData dataWithContentsOfURL:url];  // will block if url is not local!
    if (flickrJSONData) {
        flickrPropertyList = [NSJSONSerialization JSONObjectWithData:flickrJSONData
                                                                           options:0
                                                                             error:NULL];
    }
    return [flickrPropertyList valueForKeyPath:FLICKR_RESULTS_PHOTOS];
}

// gets the Flickr photo dictionaries out of the url and puts them into Core Data
// this was moved here after lecture to give you an example of how to declare a method that takes a block as an argument
// and because we now do this both as part of our background session delegate handler and when background fetch happens

- (void)loadFlickrPhotosFromLocalURL:(NSURL *)localFile
                         intoContext:(NSManagedObjectContext *)context
                 andThenExecuteBlock:(void(^)())whenDone
{
    if (context) {
        NSArray *photos = [self flickrPhotosAtURL:localFile];
        [context performBlock:^{
            [Photo loadPhotosFromFlickrArray:photos intoManagedObjectContext:context];
            [context save:NULL]; // NOT NECESSARY if this is a UIManagedDocument's context
            if (whenDone) whenDone();
        }];
    } else {
        if (whenDone) whenDone();
    }
}

#pragma mark - NSURLSessionDownloadDelegate

// required by the protocol
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)localFile
{
    // we shouldn't assume we're the only downloading going on ...
    if ([downloadTask.taskDescription isEqualToString:FLICKR_FETCH]) {
        // ... but if this is the Flickr fetching, then process the returned data
        [self loadFlickrPhotosFromLocalURL:localFile
                               intoContext:self.photoDatabaseContext
                       andThenExecuteBlock:^{
                           [self flickrDownloadTasksMightBeComplete];
                       }
         ];
    }
}

// required by the protocol
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didResumeAtOffset:(int64_t)fileOffset
expectedTotalBytes:(int64_t)expectedTotalBytes
{
    // we don't support resuming an interrupted download task
}

// required by the protocol
- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    // we don't report the progress of a download in our UI, but this is a cool method to do that with
}

// not required by the protocol, but we should definitely catch errors here
// so that we can avoid crashes
// and also so that we can detect that download tasks are (might be) complete
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    if (error && (session == self.flickrDownloadSession)) {
        NSLog(@"Flickr background download session failed: %@", error.localizedDescription);
        [self flickrDownloadTasksMightBeComplete];
    }
}

// this is "might" in case some day we have multiple downloads going on at once

- (void)flickrDownloadTasksMightBeComplete
{
    if (self.flickrDownloadBackgroundURLSessionCompletionHandler) {
        [self.flickrDownloadSession getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
            // we're doing this check for other downloads just to be theoretically "correct"
            //  but we don't actually need it (since we only ever fire off one download task at a time)
            // in addition, note that getTasksWithCompletionHandler: is ASYNCHRONOUS
            //  so we must check again when the block executes if the handler is still not nil
            //  (another thread might have sent it already in a multiple-tasks-at-once implementation)
            if (![downloadTasks count]) {  // any more Flickr downloads left?
                // nope, then invoke flickrDownloadBackgroundURLSessionCompletionHandler (if it's still not nil)
                void (^completionHandler)() = self.flickrDownloadBackgroundURLSessionCompletionHandler;
                self.flickrDownloadBackgroundURLSessionCompletionHandler = nil;
                if (completionHandler) {
                    completionHandler();
                }
            } // else other downloads going, so let them call this method when they finish
        }];
    }
}

@end

PhotosCDTVC

//
//  PhotosCDTVC.h
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "CoreDataTableViewController.h"

@interface PhotosCDTVC : CoreDataTableViewController

// generic Photo displaying CDTVC
// hook up fetchedResultsController to any Photo fetch request
// use @"Photo Cell" as your table view cell's reuse id
// will segue to showing the image in an ImageViewController

@end
//
//  PhotosCDTVC.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "PhotosCDTVC.h"
#import "Photo.h"
#import "ImageViewController.h"

@implementation PhotosCDTVC

#pragma mark - UITableViewDataSource

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"Photo Cell"];
    
    Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath];
    cell.textLabel.text = photo.title;
    cell.detailTextLabel.text = photo.subtitle;
    
    return cell;
}

#pragma mark - Navigation

- (void)prepareViewController:(id)vc
                     forSegue:(NSString *)segueIdentifer
                fromIndexPath:(NSIndexPath *)indexPath
{
    Photo *photo = [self.fetchedResultsController objectAtIndexPath:indexPath];

    // note that we don't check the segue identifier here
    // could easily imagine two different segues to ImageViewController from this class
    // for example, one might apply some sort of sepia tone or something
    // but for now, we only have this one segue, so we'll not check the segue identifier

    if ([vc isKindOfClass:[ImageViewController class]]) {
        ImageViewController *ivc = (ImageViewController *)vc;
        ivc.imageURL = [NSURL URLWithString:photo.imageURL];
        ivc.title = photo.title;
    }
}

// boilerplate
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    NSIndexPath *indexPath = nil;
    if ([sender isKindOfClass:[UITableViewCell class]]) {
        indexPath = [self.tableView indexPathForCell:sender];
    }
    [self prepareViewController:segue.destinationViewController
                       forSegue:segue.identifier
                  fromIndexPath:indexPath];
}

// boilerplate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    id detailvc = [self.splitViewController.viewControllers lastObject];
    if ([detailvc isKindOfClass:[UINavigationController class]]) {
        detailvc = [((UINavigationController *)detailvc).viewControllers firstObject];
        [self prepareViewController:detailvc
                           forSegue:nil
                      fromIndexPath:indexPath];
    }
}

@end

PhotosByPhotographerCDTVC

#import "PhotosCDTVC.h"
#import "Photographer.h"

// this class inherits the ability to display a Photo in its rows
// and the ability to navigate to show the Photo's image
// from it superclass PhotosCDTVC

@interface PhotosByPhotographerCDTVC : PhotosCDTVC

@property (nonatomic, strong) Photographer *photographer;

@end
//
//  PhotosByPhotographerCDTVC.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "PhotosByPhotographerCDTVC.h"

@implementation PhotosByPhotographerCDTVC

- (void)setPhotographer:(Photographer *)photographer
{
    _photographer = photographer;
    self.title = photographer.name;
    [self setupFetchedResultsController];
}

- (void)setupFetchedResultsController
{
    NSManagedObjectContext *context = self.photographer.managedObjectContext;
    
    if (context) {
        NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Photo"];
        request.predicate = [NSPredicate predicateWithFormat:@"whoTook = %@", self.photographer];
        request.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"title"
                                                                  ascending:YES
                                                                   selector:@selector(localizedStandardCompare:)]];
        
        self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
                                                                            managedObjectContext:context
                                                                              sectionNameKeyPath:nil
                                                                                       cacheName:nil];
    } else {
        self.fetchedResultsController = nil;
    }
}


@end

URLViewController

//
//  URLViewController.m
//  Photomania
//
//  Created by CS193p Instructor.
//  Copyright (c) 2013 Stanford University. All rights reserved.
//

#import "URLViewController.h"

@interface URLViewController ()
@property (weak, nonatomic) IBOutlet UITextView *urlTextView;
@end

@implementation URLViewController

- (void)setUrl:(NSURL *)url
{
    _url = url;
    [self updateUI];
}

- (void)viewDidLoad // updateUI here in case our url property was set before outlets loaded
{
    [super viewDidLoad];
    [self updateUI];
}

- (void)updateUI
{
    self.urlTextView.text = [self.url absoluteString];
}

@end