1

アプリでexc_bad_accessを取得していますが、修正方法がわかりません。Xcode 4.5にアップグレードしたばかりで、IOS5.0をターゲットにしています。UIPageViewControllerを使用するのもこれが初めてです。絵コンテをできるだけ使いたいです。

私がやろうとしているのは、スクロールやズームが可能な画像を含むゴルフコースのパラパラマンガを再現することです。私は基本的に、ほとんど機能しているいくつかのチュートリアルのマッシュアップを持っています。

1)UIScrollviewをロードするUIPageviewControllerをセットアップしました。これにより、imageViewが追加されます。2)ジェスチャーまたはタップによる反転が機能し、スクロールが機能し、ピンチズームが機能し、カスタムの1本指および2本指のタッピングがズームイン/ズームアウトに機能します。3)スライドジェスチャでページをめくり始めた後、指を離すとクラッシュが発生します。これは基本的にフリップをキャンセルしますが、その後、メッセージがゾンビオブジェクトに送信されます。

これが私の'GuideViewController.h'で、ルートとしてだけでなくデータソースとしても機能します。

#import <UIKit/UIKit.h>
#import "YardageHoleViewController.h"

@interface GuideViewController : UIViewController <UIPageViewControllerDataSource>

@property (strong, nonatomic) UIPageViewController *pageController;
@property (strong, nonatomic) NSArray *pageContent;

- (YardageHoleViewController *)viewControllerAtIndex:(NSUInteger)index storyboard (UIStoryboard *)storyboard;
- (NSUInteger)indexOfViewController:(YardageHoleViewController *)viewController;

@end

そしてここに実装があります

#import "GuideViewController.h"
#import "GolfCourseAppDelegate.h"
#import "Hole.h"
@interface GuideViewController ()
@end

@implementation GuideViewController
@synthesize pageContent = _pageContent;
@synthesize pageController = _pageController;

- (void)viewWillDisappear:(BOOL)animated
{
[[[GolfCourseAppDelegate sharedDelegate] locationManager] stopUpdatingLocation];
}

- (void)viewDidLoad
{
[super viewDidLoad];
[[[GolfCourseAppDelegate sharedDelegate] locationManager] startUpdatingLocation];
[self createContentPages];

NSDictionary *options =
[NSDictionary dictionaryWithObject:
 [NSNumber numberWithInteger:UIPageViewControllerSpineLocationMin]
                            forKey: UIPageViewControllerOptionSpineLocationKey];

self.pageController = [[UIPageViewController alloc]
initWithTransitionStyle:UIPageViewControllerTransitionStylePageCurl navigationOrientation:UIPageViewControllerNavigationOrientationVertical options: options];

//self.pageController.delegate = self;

self.pageController.dataSource = self;
[[self.pageController view] setFrame:[[self view] bounds]];

YardageHoleViewController *initialViewController = [self viewControllerAtIndex:0 storyboard:self.storyboard];

NSArray *viewControllers = [NSArray arrayWithObject:initialViewController];

[self.pageController setViewControllers:viewControllers
                         direction:UIPageViewControllerNavigationDirectionForward
                          animated:NO
                        completion:NULL];

[self addChildViewController:self.pageController];
[[self view] addSubview:[self.pageController view]];
[self.pageController didMoveToParentViewController:self];

}

- (YardageHoleViewController *)viewControllerAtIndex:(NSUInteger)index storyboard:(UIStoryboard *)storyboard
{
NSLog(@"getting data view controller at index: %d", index);
// Return the data view controller for the given index.
if (([self.pageContent count] == 0) || (index >= [self.pageContent count])) {
    return nil;
}

// Create a new view controller and pass suitable data.
YardageHoleViewController *yardageHoleViewController = [storyboard instantiateViewControllerWithIdentifier:@"YardageHoleViewController"];
yardageHoleViewController.dataObject = [self.pageContent objectAtIndex:index];
return yardageHoleViewController;
}

- (NSUInteger)indexOfViewController:(YardageHoleViewController *)viewController
{
// Return the index of the given data view controller.
// For simplicity, this implementation uses a static array of model objects and the view controller stores the model object; you can therefore use the model object to identify the index.
NSLog(@"returning indexOfViewController : %d", [self.pageContent indexOfObject:viewController.dataObject]);
return [self.pageContent indexOfObject:viewController.dataObject];
}

#pragma mark - Page View Controller Data Source

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
NSLog(@"getting view controller before view controller");
NSUInteger index = [self indexOfViewController:(YardageHoleViewController *)viewController];
if ((index == 0) || (index == NSNotFound)) {
    return nil;
}

index--;
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}

- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
NSLog(@"getting view controller After view controller");
NSUInteger index = [self indexOfViewController:(YardageHoleViewController *)viewController];
if (index == NSNotFound) {
    return nil;
}

index++;
if (index == [self.pageContent count]) {
    return nil;
}
return [self viewControllerAtIndex:index storyboard:viewController.storyboard];
}

- (void) createContentPages
{
NSLog(@"creating content Pages");
int totalHoles = [[[GolfCourseAppDelegate appData] objectForKey:@"holes"] count];

NSMutableArray *holeData = [[NSMutableArray alloc] init];
for (int i = 1; i < totalHoles+1; i++)
{
    Hole *newHole = [[Hole alloc] initWithHoleNumber:i imageUrl:[NSString stringWithFormat:@"hole%@%d.jpg", (i < 10) ? @"0" : @"", i]];
    NSLog(@"Hole image url:%@",newHole.imageUrl);
    //int holeNumber = i;
    //NSString *imageUrl = [NSString stringWithFormat:@"hole%@%d.jpg", (i < 10) ? @"0" : @"", i];
   [holeData addObject:newHole];
}

self.pageContent = [[NSArray alloc] initWithArray:holeData];
NSLog(@"count of holeData %d", self.pageContent.count);
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

ビューについては、「YardageHoleViewController.h」をめくっています。

#import <UIKit/UIKit.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CoreLocation/CoreLocation.h>

@interface YardageHoleViewController : UIViewController <UIScrollViewDelegate, CLLocationManagerDelegate>

@property (strong, nonatomic) IBOutlet UIScrollView *scrollView;
@property (assign, nonatomic) int hole;
@property (assign, nonatomic) int totalHoles;
@property (strong, nonatomic) id dataObject;
@property (strong, nonatomic) IBOutlet UILabel *frontLabel;
@property (strong, nonatomic) IBOutlet UILabel *middleLabel;
@property (strong, nonatomic) IBOutlet UILabel *backLabel;
- (IBAction)nextPage:(id)sender;
- (IBAction)previousPage:(id)sender;
- (IBAction)infoPage:(id)sender;
- (IBAction)homePage:(id)sender;

- (void)updateDistanceDisplay;
- (NSString *)formatDistance:(NSNumber *)distance;

@end

ここで、私がいくつかのことを行っていることがわかります。場所などに基づいてカップまでの距離を示すいくつかのサブビューがあります。いくつかのアウトレットも表示されます。ジェスチャーに加えてナビゲートするためのボタンを上部に配置したかったのですが、ジェスチャーがボタンタップをオーバーライドしているため、現在は機能していません。 (後で別の質問)。

これが肉とジャガイモの「YardageHoleViewController.m」です

#import "YardageHoleViewController.h"
#import "GolfCourseAppDelegate.h"
#import "Hole.h"

@interface YardageHoleViewController ()
@property (nonatomic, strong) UIImageView *imageView;
- (void)centerScrollViewContents;
- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer;
- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer;
@end

@implementation YardageHoleViewController
@synthesize scrollView = _scrollView;
@synthesize hole = _hole;
@synthesize totalHoles = _totalHoles;
@synthesize imageView = _imageView;
@synthesize frontLabel = _frontLabel;
@synthesize middleLabel = _middleLabel;
@synthesize backLabel = _backLabel;
@synthesize dataObject = _dataObject;

/* The point of this method is to get around a slight annoyance with UIScrollView, which is: if the scroll view content size is smaller than its bounds, then it sits at the top-left rather than in the center. This method  positions the image view such that it is always in the center of the scroll view’s bounds.
 */
- (void)centerScrollViewContents {
CGSize boundsSize = self.scrollView.bounds.size;
CGRect contentsFrame = self.imageView.frame;

if (contentsFrame.size.width < boundsSize.width) {
    contentsFrame.origin.x = (boundsSize.width - contentsFrame.size.width) / 2.0f;
} else {
    contentsFrame.origin.x = 0.0f;
}

if (contentsFrame.size.height < boundsSize.height) {
    contentsFrame.origin.y = (boundsSize.height - contentsFrame.size.height) / 2.0f;
} else {
    contentsFrame.origin.y = 0.0f;
}

self.imageView.frame = contentsFrame;
}

- (void)scrollViewDoubleTapped:(UITapGestureRecognizer*)recognizer {
CGPoint pointInView = [recognizer locationInView:self.imageView];
CGFloat newZoomScale = self.scrollView.zoomScale * 1.5f;
newZoomScale = MIN(newZoomScale, self.scrollView.maximumZoomScale);
CGSize scrollViewSize = self.scrollView.bounds.size;
CGFloat w = scrollViewSize.width / newZoomScale;
CGFloat h = scrollViewSize.height / newZoomScale;
CGFloat x = pointInView.x - (w / 2.0f);
CGFloat y = pointInView.y - (h / 2.0f);
CGRect rectToZoomTo = CGRectMake(x, y, w, h);
[self.scrollView zoomToRect:rectToZoomTo animated:YES];
}

- (void)scrollViewTwoFingerTapped:(UITapGestureRecognizer*)recognizer {
// Zoom out slightly, capping at the minimum zoom scale specified by the scroll view
CGFloat newZoomScale = self.scrollView.zoomScale / 1.5f;
newZoomScale = MAX(newZoomScale, self.scrollView.minimumZoomScale);
[self.scrollView setZoomScale:newZoomScale animated:YES];
}

- (UIView*)viewForZoomingInScrollView:(UIScrollView *)scrollView {
// Return the view that you want to zoom
return self.imageView;
}

- (void)scrollViewDidZoom:(UIScrollView *)scrollView {
// The scroll view has zoomed, so you need to re-center the contents
[self centerScrollViewContents];
}

- (void)viewDidLoad {
[super viewDidLoad];
Hole *hole = (Hole*)self.dataObject;
self.hole = hole.holeNumber;
UIImage *image = [UIImage imageNamed:hole.imageUrl];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.imageView.frame = (CGRect){.origin=CGPointMake(0.0f, 0.0f), .size=image.size};
[self.scrollView addSubview:self.imageView];
self.scrollView.contentSize = image.size;
//Here you’re setting up two gesture recognizers: one for the double-tap to zoom in, and one for the two-finger-tap to zoom out.
UITapGestureRecognizer *doubleTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewDoubleTapped:)];
doubleTapRecognizer.numberOfTapsRequired = 2;
doubleTapRecognizer.numberOfTouchesRequired = 1;
[self.scrollView addGestureRecognizer:doubleTapRecognizer];

UITapGestureRecognizer *twoFingerTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(scrollViewTwoFingerTapped:)];
twoFingerTapRecognizer.numberOfTapsRequired = 1;
twoFingerTapRecognizer.numberOfTouchesRequired = 2;
[self.scrollView addGestureRecognizer:twoFingerTapRecognizer];
[[[GolfCourseAppDelegate sharedDelegate] locationManager] setDelegate:self];
[self updateDistanceDisplay];

self.totalHoles = [[[GolfCourseAppDelegate appData] objectForKey:@"holes"] count];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didRotate:) name:@"UIDeviceOrientationDidChangeNotification" object:nil];
}

- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];

CGRect scrollViewFrame = self.scrollView.frame;
CGFloat scaleWidth = scrollViewFrame.size.width / self.scrollView.contentSize.width;
self.scrollView.minimumZoomScale = scaleWidth;
self.scrollView.maximumZoomScale = 1.5f;
self.scrollView.zoomScale = scaleWidth;
[self centerScrollViewContents];
}

- (void) didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];

if (orientation == UIDeviceOrientationLandscapeLeft || orientation == UIDeviceOrientationLandscapeRight) {
    [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleBlackTranslucent];
    NSString *moviePath = [[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"hole%@%d", (self.hole < 10) ? @"0" : @"", self.hole] ofType:@"mp4"];
    MPMoviePlayerViewController *viewController = [[MPMoviePlayerViewController alloc] initWithContentURL:[NSURL fileURLWithPath:moviePath]];
    viewController.moviePlayer.controlStyle = MPMovieControlStyleNone;
    viewController.view.backgroundColor = [UIColor blackColor];
    [self presentMoviePlayerViewControllerAnimated:viewController];
} else {
    [self dismissMoviePlayerViewControllerAnimated];
}
}

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
[self updateDistanceDisplay];
}

- (void) updateDistanceDisplay {

CLLocation *userLocation = [[GolfCourseAppDelegate sharedDelegate] userLocation];

if (userLocation != nil) {
    NSMutableDictionary *holeLocations = [[[GolfCourseAppDelegate appData] objectForKey:@"holes"] objectForKey:[NSString stringWithFormat:@"hole%d", self.hole]];

    if (round([[[holeLocations objectForKey:@"front"] objectForKey:@"lat"] floatValue]) == 0) {
        self.frontLabel.text = @"---";
    } else {
        CLLocation *frontLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:@"front"] objectForKey:@"lat"] floatValue] longitude:[[[holeLocations objectForKey:@"front"] objectForKey:@"lng"] floatValue]];
        if (([frontLocation distanceFromLocation:userLocation]/1000)>1000){
            self.frontLabel.text = @"Out of Range";
        }else{
        self.frontLabel.text = [self formatDistance:[NSNumber numberWithFloat:([frontLocation distanceFromLocation:userLocation]/1000)]];
        }
    }

    if (round([[[holeLocations objectForKey:@"middle"] objectForKey:@"lat"] floatValue]) == 0) {
        self.middleLabel.text = @"---";
    } else {
        CLLocation *middleLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:@"middle"] objectForKey:@"lat"] floatValue] longitude:[[[holeLocations objectForKey:@"middle"] objectForKey:@"lng"] floatValue]];
        self.middleLabel.text = [self formatDistance:[NSNumber numberWithFloat:([middleLocation distanceFromLocation:userLocation]/1000)]];

    }

    if (round([[[holeLocations objectForKey:@"back"] objectForKey:@"lat"] floatValue]) == 0) {
        self.backLabel.text = @"---";
    } else {
        CLLocation *backLocation = [[CLLocation alloc] initWithLatitude:[[[holeLocations objectForKey:@"back"] objectForKey:@"lat"] floatValue] longitude:[[[holeLocations objectForKey:@"back"] objectForKey:@"lng"] floatValue]];
        self.backLabel.text = [self formatDistance:[NSNumber numberWithFloat:([backLocation distanceFromLocation:userLocation]/1000)]];

    }
}
}

- (NSString *) formatDistance:(NSNumber *)distance {

NSNumber *displayDistance;
NSString *unitSuffix = @"";

// Convert km to yards if prefs say so.
NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults];

if ([[preferences stringForKey:@"measurementUnit"] isEqualToString:@"meters"]) {

    distance = [NSNumber numberWithFloat:([distance floatValue]*1000.0)];
    if ([distance floatValue] < 1000.0) {
        displayDistance = distance;
        unitSuffix = @"";
    } else {
        displayDistance = [NSNumber numberWithFloat:([distance floatValue]/1000.0)];
        unitSuffix = @"km";
    }

} else {

    distance = [NSNumber numberWithFloat:([distance floatValue]*1.0936133*1000.0)];
    if ([distance floatValue] < 1760.0) {
        displayDistance = distance;
        unitSuffix = @"";
    } else {
        displayDistance = [NSNumber numberWithFloat:([distance floatValue]/1760.0)];
        unitSuffix = @"mi";
    }

}

NSNumberFormatter *decimalStyle = [[NSNumberFormatter alloc] init];
[decimalStyle setFormatterBehavior:NSNumberFormatterBehavior10_4];
[decimalStyle setNumberStyle:NSNumberFormatterDecimalStyle];
[decimalStyle setRoundingMode:NSNumberFormatterRoundFloor];
[decimalStyle setRoundingIncrement:[NSNumber numberWithFloat:1.0]];

NSString *finalDistance = [decimalStyle stringFromNumber:displayDistance];

return [NSString stringWithFormat:@"%@%@", finalDistance, unitSuffix];
}

- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}

- (void)viewDidUnload {
[self setImageView:nil];
[self setScrollView:nil];
[self setFrontLabel:nil];
[self setBackLabel:nil];
[self setBackLabel:nil];
[self setFrontLabel:nil];
[self setMiddleLabel:nil];
[super viewDidUnload];
}
- (IBAction)nextPage:(id)sender {
//TODO
// [((UIPageViewController*)self.parentViewController) setViewControllers:
// target direction:UIPageViewControllerNavigationForward completion:nil];
}

- (IBAction)previousPage:(id)sender {
//TODO
//  [((UIPageViewController*)self.parentViewController) setViewControllers:<#(NSArray *)#> direction:UIPageViewControllerNavigationDirectionReverse animated:true completion:nil];
}

- (IBAction)infoPage:(id)sender {
//TODO
}

- (IBAction)homePage:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}
@end

ふぅ!たくさんの読書。そこで、最初に試したのは、例外ブレークポイントを設定することでした。運がない。次に、クラッシュする場所を確認するためにNSlogステートメントをたくさん追加し、最後に楽器のゾンビを探しました。ここでは、mallocで、「Objective-CメッセージがYardageHoleViewControllerのアドレス0x1386e0e0にある割り当て解除されたオブジェクト(ゾンビ)に送信されました。

NSLogステートメントから、成功したページめくりは次のようになっていることがわかります。2012-12-16 13:33:52.280 BAPテンプレート[1365:13a03]インデックスでデータビューコントローラーを取得:0//ここでフリップを開始>2012-12-16 13:34:06.289BAPテンプレート[1365:13a03]取得ビューコントローラービューコントローラーの後2012-12-1613:34:06.290 BAPテンプレート[1365:13a03] return indexOfViewController:0 2012-12-16 13:34:06.292 BAPテンプレート[1365:13a03]インデックスでデータビューコントローラーを取得: 1

フリップを開始してからリリース2012-12-1613:36:18.613 BAP Template [1365:13a03]インデックスでデータビューコントローラーを取得すると、次のようになります。0//フリップを開始してからリリース2012-12-1613: 36:21.828BAPテンプレート[1365:13a03]ビューコントローラーの取得ビューコントローラーの後2012-12-16 13:36:21.829 BAPテンプレート[1365:13a03] return indexOfViewController:0 2012-12-16 13:36:21.831BAPテンプレート[ 1365:13a03]インデックスでデータビューコントローラを取得しています:1

したがって、ある意味では、フリップが完了したように動作しようとしていますが、そうではありませんでした。それは、悪い時間があるときです=(

私はすべてを強く設定しました、そして私は本当に次に何を試すべきかわかりませんか?

一般的な私のコードに関する提案は本当にありがたいです。前もって感謝します! ゾンビのプロフィール

更新オーガナイザーでクラッシュログを確認しましたスレッド0名:ディスパッチキュー:com.apple.main-スレッドスレッド0クラッシュ:0 libobjc.A.dylib 0x3737bf78 objc_msgSend + 16 1 CoreLocation 0x3405ddc0-[CLLocationManager onClientEventLocation:] + 1136 2 CoreLocation 0x3405d77e-[CLLocationManager onClientEvent:supportInfo:] + 194 3 CoreLocation 0x34057e38 __CLClientInvokeCallback_block_invoke_0 + 48それ以来、guideViewControllerのviewDidLoad/willDisappearで位置の更新を開始/停止する2行をコメントアウトしました。

これ以上クラッシュすることはありませんが、なぜですか?

4

1 に答える 1

1

ビューの読み込み時に位置情報の更新が開始されるため、この方法

- (void)locationManager:didUpdateToLocation:fromLocation:

毎秒のように、シミュレーターでは継続的に呼び出されますが、デバイスでは、動きを検出したときにのみ呼び出されます。そのメソッドの中には[selfupdateDistanceDisplay]の呼び出しがあり、それがクラッシュしていた理由です。私が実装した修正は、現在デバイスで正常に機能しますが、防弾ではありません。

まず、appDelegateで、locationManager.distanceFilterを修正して、すべての小さな動きがデリゲートメソッドをトリガーしないようにします。

self.locationManager.distanceFilter = 1.0f;

次に、緯度または経度に変更があった場合にのみ表示を更新するように、didUpdateToLocationメソッドを変更します。

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
   if ((newLocation.coordinate.latitude!=oldLocation.coordinate.latitude)||(newLocation.coordinate.longitude!=oldLocation.coordinate.longitude))
       [self updateDistanceDisplay];
}

繰り返しますが、防弾ではありません。ユーザーがゴルフカートを十分に速く移動し、ページをめくろうとすると、クラッシュ状態になる可能性がある場合があります。

于 2012-12-20T18:27:55.767 に答える