简体   繁体   中英

how to read data from a plist saved in document directory and display that as pdf in a new view?

So I save user input data in a plist in my document directory. data is consist of a few strings. such as text and image. the user inputs the data in a add view through textfields and the image through the camera or photo album.

I use the following for saving the data in add view controller:

// Name for the image
    NSString *imageName;

    if (self.image) {
        // Create a unique name for the image by generating a UUID, converting it to
        // a string, and appending the .jpg extension.
        CFUUIDRef imageUUID = CFUUIDCreate(kCFAllocatorDefault);
        imageName = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, imageUUID));
        CFRelease(imageUUID);
        imageName = [imageName stringByAppendingString:@".jpg"];

        // Lookup the URL for the Documents folder
        NSURL *imageFileURL = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0];
        // Append the file name to create the complete URL for saving the image.
        imageFileURL = [imageFileURL URLByAppendingPathComponent:imageName isDirectory:NO];

        // Convert the image to JPG format and write the data to disk at the above URL.
        [UIImageJPEGRepresentation(self.image, 1.0f) writeToURL:imageFileURL atomically:YES];
    } else {
        // If there is no image, we must make sure imageName is not nil.
        imageName = @"";
    }
#define PLIST_NAME @"Data.plist"
    [self createPlistCopyInDocuments:PLIST_NAME];
    NSString *filePath = [self plistFileDocumentPath:PLIST_NAME];
    NSMutableArray *dataArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
    NSDictionary *data = @{@"city":self.name,@"state":self.stateTextField.text,@"cityPrice":self.priceTextField.text,@"cityText":self.cityDescription.text, @"cityImage": imageName};
    [dataArray addObject:data];
    [dataArray writeToFile:filePath atomically:YES];

then i add the following method for the document path:

- (NSString *)plistFileDocumentPath:(NSString *)plistName
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:plistName];
    return writablePath;
}

- (void)createPlistCopyInDocuments:(NSString *)plistName
{
    // First, test for existence.
    BOOL success;

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSError *error;
    NSString *plistFilePath = [self plistFileDocumentPath:plistName];
    success = [fileManager fileExistsAtPath:plistFilePath];

    if (success) {
        return;
    }

    // The writable file does not exist, so copy from the bundle to the appropriate location.
    NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:plistName];
    success = [fileManager copyItemAtPath:defaultPath toPath:plistFilePath error:&error];
    if (!success) {
        NSAssert1(0, @"Failed to create writable file with message '%@'.", [error localizedDescription]);
    }
}

then in my tableview controller i read the data and display that as follows:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    NSString *filePath = [self plistFileDocumentPath:@"Data.plist"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    BOOL exist = [fileManager fileExistsAtPath:filePath];
    if (!exist) {
        return;
    }
    NSMutableArray *dataArray = [[NSMutableArray alloc] initWithContentsOfFile:filePath];
    content = dataArray;
    [self.tableView reloadData];
}

- (NSString *)plistFileDocumentPath:(NSString *)plistName
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:plistName];
    return writablePath;
}

i do have the plist in view did load as follows:

content = [[NSMutableArray alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Data" ofType:@"plist"]];

i also can display all the info properly in my detail view. i created a new view in order to display the data saved in the plist as a pdf file. i am able to retrieve the data from a existing plist in my directory but i can't convert that code to read the data from my saved plist.

this is how i create the pdf from that plist.

    - (IBAction)pdfPressed:(id)sender {


    // create some sample data. In a real application, this would come from the database or an API.
    NSString* path = [[NSBundle mainBundle] pathForResource:@"sampleData" ofType:@"plist"];
    NSDictionary* data = [NSDictionary dictionaryWithContentsOfFile:path];
    NSArray* students = [data objectForKey:@"Students"];

    // get a temprorary filename for this PDF
    path = NSTemporaryDirectory();
    self.pdfFilePath = [path stringByAppendingPathComponent:[NSString stringWithFormat:@"%f.pdf", [[NSDate date] timeIntervalSince1970] ]];

    // Create the PDF context using the default page size of 612 x 792.
    // This default is spelled out in the iOS documentation for UIGraphicsBeginPDFContextToFile
    UIGraphicsBeginPDFContextToFile(self.pdfFilePath, CGRectZero, nil);

    // get the context reference so we can render to it.
    CGContextRef context = UIGraphicsGetCurrentContext();

    int currentPage = 0;

    // maximum height and width of the content on the page, byt taking margins into account.
    CGFloat maxWidth = kDefaultPageWidth - kMargin * 2;
    CGFloat maxHeight = kDefaultPageHeight - kMargin * 2;

    // we're going to cap the name of the class to using half of the horizontal page, which is why we're dividing by 2
    CGFloat classNameMaxWidth = maxWidth / 2;

    // the max width of the grade is also half, minus the margin
    CGFloat gradeMaxWidth = (maxWidth / 2) - kColumnMargin;


    // only create the fonts once since it is a somewhat expensive operation
    UIFont* studentNameFont = [UIFont boldSystemFontOfSize:17];
    UIFont* classFont = [UIFont systemFontOfSize:15];

    CGFloat currentPageY = 0;

    // iterate through out students, adding to the pdf each time.
    for (NSDictionary* student in students)
    {
        // every student gets their own page
        // Mark the beginning of a new page.
        UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth, kDefaultPageHeight), nil);
        currentPageY = kMargin;

        // draw the student's name at the top of the page.
        NSString* name = [NSString stringWithFormat:@"%@ %@",
                          [student objectForKey:@"FirstName"],
                          [student objectForKey:@"LastName"]];

        CGSize size = [name sizeWithFont:studentNameFont forWidth:maxWidth lineBreakMode:NSLineBreakByWordWrapping];
        [name drawAtPoint:CGPointMake(kMargin, currentPageY) forWidth:maxWidth withFont:studentNameFont lineBreakMode:NSLineBreakByWordWrapping];
        currentPageY += size.height;

        // draw a one pixel line under the student's name
        CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
        CGContextMoveToPoint(context, kMargin, currentPageY);
        CGContextAddLineToPoint(context, kDefaultPageWidth - kMargin, currentPageY);
        CGContextStrokePath(context);

        // iterate through the list of classes and add these to the PDF.
        NSArray* classes = [student objectForKey:@"Classes"];
        for(NSDictionary* class in classes)
        {
            NSString* className = [class objectForKey:@"Name"];
            NSString* grade = [class objectForKey:@"Grade"];

            // before we render any text to the PDF, we need to measure it, so we'll know where to render the
            // next line.
            size = [className sizeWithFont:classFont constrainedToSize:CGSizeMake(classNameMaxWidth, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];

            // if the current text would render beyond the bounds of the page,
            // start a new page and render it there instead
            if (size.height + currentPageY > maxHeight) {
                // create a new page and reset the current page's Y value
                UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth, kDefaultPageHeight), nil);
                currentPageY = kMargin;
            }

            // render the text
            [className drawInRect:CGRectMake(kMargin, currentPageY, classNameMaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];

            // print the grade to the right of the class name
            [grade drawInRect:CGRectMake(kMargin + classNameMaxWidth + kColumnMargin, currentPageY, gradeMaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];

            currentPageY += size.height;

        }


        // increment the page number.
        currentPage++;

    }

    // end and save the PDF.
    UIGraphicsEndPDFContext();

    // Ask the user if they'd like to see the file or email it.
    UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle:@"Would you like to preview or email this PDF?"
                                                              delegate:self
                                                     cancelButtonTitle:@"Cancel"
                                                destructiveButtonTitle:nil
                                                     otherButtonTitles:@"Preview", @"Email", nil];
    [actionSheet showInView:self.view];
}

Can someone look at the above given information and help me change this create pdf code to read the data i am saving to the plist in my document directory please. here is the link to the demo project in github for anyone that wants to lend a helping hand.:)

Additional info:

I am saving the user input data in plist which the root is an array. the code I am using to render the PDF file is from a plist that the root is dictionary. I need help with changing the code in pdfpressed to read the plist that the root would be an array not a dictionary.

在此处输入图片说明

Edit:

i used the fix that was posted and the whole method looks like this now:

    - (IBAction)pdfPressed:(id)sender {

     NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ,NSUserDomainMask, YES);
     NSString *documentsDirectory = [sysPaths objectAtIndex:0];
     NSString *filePath =  [documentsDirectory stringByAppendingPathComponent:@"Data1.plist"];

     NSLog(@"File Path: %@", filePath);

     NSArray *students = [NSArray arrayWithContentsOfFile:filePath];

    // get a temprorary filename for this PDF
    filePath = NSTemporaryDirectory();
    self.pdfFilePath = [filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%f.pdf", [[NSDate date] timeIntervalSince1970] ]];

    // Create the PDF context using the default page size of 612 x 792.
    // This default is spelled out in the iOS documentation for UIGraphicsBeginPDFContextToFile
    UIGraphicsBeginPDFContextToFile(self.pdfFilePath, CGRectZero, nil);

    // get the context reference so we can render to it.
    CGContextRef context = UIGraphicsGetCurrentContext();

    int currentPage = 0;

    // maximum height and width of the content on the page, byt taking margins into account.
    CGFloat maxWidth = kDefaultPageWidth - kMargin * 2;
    CGFloat maxHeight = kDefaultPageHeight - kMargin * 2;

    // we're going to cap the name of the class to using half of the horizontal page, which is why we're dividing by 2
    CGFloat classNameMaxWidth = maxWidth / 2;

    // the max width of the grade is also half, minus the margin
    CGFloat gradeMaxWidth = (maxWidth / 2) - kColumnMargin;


    // only create the fonts once since it is a somewhat expensive operation
    UIFont* studentNameFont = [UIFont boldSystemFontOfSize:17];
    UIFont* classFont = [UIFont systemFontOfSize:15];

    CGFloat currentPageY = 0;

    // iterate through out students, adding to the pdf each time.
    for (NSDictionary* student in students)
    {
        // every student gets their own page
        // Mark the beginning of a new page.
        UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth, kDefaultPageHeight), nil);
        currentPageY = kMargin;

        // draw the student's name at the top of the page.
        NSString* name = [NSString stringWithFormat:@"%@ %@ %@ %@", [student valueForKey:@"city"], [student valueForKey:@"state"],[student valueForKey:@"cityPrice"],[student valueForKey:@"cityText"]];

        CGSize size = [name sizeWithFont:studentNameFont forWidth:maxWidth lineBreakMode:NSLineBreakByWordWrapping];
        [name drawAtPoint:CGPointMake(kMargin, currentPageY) forWidth:maxWidth withFont:studentNameFont lineBreakMode:NSLineBreakByWordWrapping];
        currentPageY += size.height;

        // draw a one pixel line under the student's name
        CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
        CGContextMoveToPoint(context, kMargin, currentPageY);
        CGContextAddLineToPoint(context, kDefaultPageWidth - kMargin, currentPageY);
        CGContextStrokePath(context);

        // iterate through the list of classes and add these to the PDF.
        NSArray* classes = [student objectForKey:@"Classes"];
        for(NSDictionary* class in classes)
        {
            NSString* className = [class objectForKey:@"Name"];
            NSString* grade = [class objectForKey:@"Grade"];

            // before we render any text to the PDF, we need to measure it, so we'll know where to render the
            // next line.
            size = [className sizeWithFont:classFont constrainedToSize:CGSizeMake(classNameMaxWidth, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];

            // if the current text would render beyond the bounds of the page,
            // start a new page and render it there instead
            if (size.height + currentPageY > maxHeight) {
                // create a new page and reset the current page's Y value
                UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth, kDefaultPageHeight), nil);
                currentPageY = kMargin;
            }

            // render the text
            [className drawInRect:CGRectMake(kMargin, currentPageY, classNameMaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];

            // print the grade to the right of the class name
            [grade drawInRect:CGRectMake(kMargin + classNameMaxWidth + kColumnMargin, currentPageY, gradeMaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];

            currentPageY += size.height;

        }
        // increment the page number.
        currentPage++;
    }

    // end and save the PDF.
    UIGraphicsEndPDFContext();

    // Ask the user if they'd like to see the file or email it.
    UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle:@"Would you like to preview or email this PDF?"
                                                              delegate:self
                                                     cancelButtonTitle:@"Cancel"
                                                destructiveButtonTitle:nil
                                                     otherButtonTitles:@"Preview", @"Email", nil];
    [actionSheet showInView:self.view];
}

it does render the data but it is all drawing as the header and creates one page per item. here is the screen shot

在此处输入图片说明

can you help out with separating the info and make them appear in one page.

edit 1:

here is what i am trying to do:

在此处输入图片说明

and here is the plist structure:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
    <dict>
        <key>city</key>
        <string>asd</string>
        <key>state</key>
        <string>10</string>
        <key>cituPrice</key>
        <string>11</string>
        <key>cityQuantity</key>
        <string>12</string>
        <key>cityVintage</key>
        <string>13</string>
    </dict>
    <dict>
        <key>city</key>
        <string>te2</string>
        <key>state</key>
        <string>1</string>
        <key>cityPrice</key>
        <string>2</string>
        <key>cityQuantity</key>
        <string>3</string>
        <key>cityVintage</key>
        <string>4</string>
    </dict>
    <dict>
        <key>city</key>
        <string>3434</string>
        <key>state</key>
        <string>6</string>
        <key>cityPrice</key>
        <string>7</string>
        <key>cityQuantity</key>
        <string>8</string>
        <key>cityVintage</key>
        <string>9</string>
    </dict>
    <dict>
        <key>city</key>
        <string>test 1</string>
        <key>state</key>
        <string>20</string>
        <key>cityPrice</key>
        <string>30</string>
        <key>cityQuantity</key>
        <string>44</string>
        <key>cityVintage</key>
        <string>55</string>
    </dict>
</array>
</plist>

that is how the structure of the plist would be when it is saved in the documents.

Updated Solution Replace the method in pdfgenerate

- (IBAction)pdfPressed:(id)sender {

     NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ,NSUserDomainMask, YES);
     NSString *documentsDirectory = [sysPaths objectAtIndex:0];
     NSString *filePath =  [documentsDirectory stringByAppendingPathComponent:@"Data.plist"];

     NSLog(@"File Path: %@", filePath);

     NSArray *students = [NSArray arrayWithContentsOfFile:filePath];

    // get a temprorary filename for this PDF
    filePath = NSTemporaryDirectory();
    self.pdfFilePath = [filePath stringByAppendingPathComponent:[NSString stringWithFormat:@"%f.pdf", [[NSDate date] timeIntervalSince1970] ]];

    // Create the PDF context using the default page size of 612 x 792.
    // This default is spelled out in the iOS documentation for UIGraphicsBeginPDFContextToFile
    UIGraphicsBeginPDFContextToFile(self.pdfFilePath, CGRectZero, nil);

    // get the context reference so we can render to it.
    CGContextRef context = UIGraphicsGetCurrentContext();

    int currentPage = 0;

    // maximum height and width of the content on the page, byt taking margins into account.
    CGFloat maxWidth = kDefaultPageWidth - kMargin * 2;
    CGFloat maxHeight = kDefaultPageHeight - kMargin * 2;

    // we're going to cap the name of the class to using half of the horizontal page, which is why we're dividing by 2
    CGFloat classNameMaxWidth = maxWidth / 2;

    // the max width of the grade is also half, minus the margin
    CGFloat gradeMaxWidth = (maxWidth / 2) - kColumnMargin;
    CGFloat grade1MaxWidth = (maxWidth / 2) - kColumnMargin;
    CGFloat grade2MaxWidth = (maxWidth / 2) - kColumnMargin;


    // only create the fonts once since it is a somewhat expensive operation
    UIFont* studentNameFont = [UIFont boldSystemFontOfSize:17];
    UIFont* classFont = [UIFont systemFontOfSize:15];

    CGFloat currentPageY = 0;
    UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth, kDefaultPageHeight), nil);
    currentPageY = kMargin;
    // iterate through out students, adding to the pdf each time.
    for (NSDictionary* student in students)
    {
        // every student gets their own page
        // Mark the beginning of a new page.


        // draw the student's name at the top of the page.
        NSString* name = [NSString stringWithFormat:@"%@",
                          [student valueForKey:@"city"]];

        CGSize HeaderSize = [name sizeWithFont:studentNameFont forWidth:maxWidth lineBreakMode:NSLineBreakByWordWrapping];


        // iterate through the list of classes and add these to the PDF.
        //NSArray* classes = [student objectForKey:@"Classes"];

            NSString* className = [student valueForKey:@"state"];
            NSString* grade = [student valueForKey:@"cityPrice"];
            NSString* grade1 = [student valueForKey:@"cityText"];
            NSString* grade2 = [student valueForKey:@"cityQuantity"];

            // before we render any text to the PDF, we need to measure it, so we'll know where to render the
            // next line.
           CGSize DetailSize = [className sizeWithFont:classFont constrainedToSize:CGSizeMake(classNameMaxWidth, MAXFLOAT) lineBreakMode:NSLineBreakByWordWrapping];

            // if the current text would render beyond the bounds of the page,
            // start a new page and render it there instead

        if (HeaderSize.height + DetailSize.height+currentPageY > maxHeight) {
            // create a new page and reset the current page's Y value
            UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, kDefaultPageWidth, kDefaultPageHeight), nil);
            currentPageY = kMargin;
            currentPage++;
        }
        [name drawAtPoint:CGPointMake(kMargin, currentPageY) forWidth:maxWidth withFont:studentNameFont lineBreakMode:NSLineBreakByWordWrapping];


        // draw a one pixel line under the student's name
        CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
        CGContextMoveToPoint(context, kMargin, currentPageY+HeaderSize.height);
        CGContextAddLineToPoint(context, kDefaultPageWidth - kMargin, currentPageY+HeaderSize.height);
        CGContextStrokePath(context);

        // render the text
        [className drawInRect:CGRectMake(kMargin, currentPageY+HeaderSize.height, classNameMaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentLeft];

        // print the grade to the center of the class name
        [grade drawInRect:CGRectMake (kMargin , currentPageY+HeaderSize.height, gradeMaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentCenter];
        // print the grade1 to the right of the class name
        [grade1 drawInRect:CGRectMake(kMargin  , currentPageY+HeaderSize.height, grade1MaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentRight];

        [grade2 drawInRect:CGRectMake(kMargin  , currentPageY+HeaderSize.height, grade2MaxWidth, maxHeight) withFont:classFont lineBreakMode:NSLineBreakByWordWrapping alignment:NSTextAlignmentRight];
        // increment the page number.
         currentPageY = currentPageY+DetailSize.height+HeaderSize.height+30;

    }

    // end and save the PDF.
    UIGraphicsEndPDFContext();

    // Ask the user if they'd like to see the file or email it.
    UIActionSheet* actionSheet = [[UIActionSheet alloc] initWithTitle:@"Would you like to preview or email this PDF?"
                                                              delegate:self
                                                     cancelButtonTitle:@"Cancel"
                                                destructiveButtonTitle:nil
                                                     otherButtonTitles:@"Preview", @"Email", nil];
    [actionSheet showInView:self.view];





}

OLD Solution

   NSArray *sysPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory ,NSUserDomainMask, YES);
        NSString *documentsDirectory = [sysPaths objectAtIndex:0];
        NSString *filePath =  [documentsDirectory stringByAppendingPathComponent:@"Data1.plist"];

        NSLog(@"File Path: %@", filePath);

       NSArray *students = [NSArray arrayWithContentsOfFile:filePath];

You have to get the value for specific key not an object for that key

[student objectForKey:@"city"];

Instead of the above line use it like this

[student valueForKey:@"city"];

You storing the data as Array so you can get the array of content, The above code will help you to fetch the array of object in student variable as you did in pdfpressed, the you can proceed the same to draw the content in pdf file.

在此处输入图片说明

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM