我的应用程序中出现了 exc_bad_access,但我不知道如何修复它。我刚刚升级到 Xcode 4.5,我的目标是 IOS 5.0。这也是我第一次使用 UIPageViewController。我更喜欢尽可能多地使用故事板。
我正在尝试做的是重新创建一个高尔夫球场翻书,其中包含可以滚动和缩放的图像。我现在基本上已经有了几个主要工作的教程的混搭;
1)我设置了一个 UIPageviewController,它加载了一个 UIScrollview,它向它添加了一个 imageView。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 语句来查看我们崩溃的位置,最后在仪器中寻找僵尸。在这里,我们在 YardageHoleViewController 中的 malloc 上看到“一条 Objective-C 消息已发送到地址为 0x1386e0e0 的已释放对象(僵尸)”。
从我的 NSLog 语句中,我可以看到成功的翻页看起来像这样;2012-12-16 13:33:52.280 BAP 模板 [1365:13a03] 获取索引处的数据视图控制器:0 //从此处开始翻转 > 2012-12-16 13:34:06.289 BAP 模板 [1365:13a03] 获取视图控制器在视图控制器 2012-12-16 13:34:06.290 BAP 模板 [1365:13a03] 返回 indexOfViewController 之后:0 2012-12-16 13:34:06.292 BAP 模板 [1365:13a03] 在索引处获取数据视图控制器: 1
这是当您开始翻转然后发布 2012-12-16 13:36:18.613 BAP Template[1365:13a03] 在索引处获取数据视图控制器时发生的情况:0 //开始翻转然后发布 2012-12-16 13: 36:21.828 BAP 模板 [1365:13a03] 获取视图控制器后视图控制器 2012-12-16 13:36:21.829 BAP 模板 [1365:13a03] 返回 indexOfViewController:0 2012-12-16 13:36:21.831 BAP 模板 [ 1365:13a03] 在索引处获取数据视图控制器:1
所以从某种意义上说,它试图表现得好像它完成了翻转,但我们没有,然后就是我们遇到了糟糕的时刻 =(
我已将所有内容设置为强,我真的不知道接下来要尝试什么?
一般来说,对我的代码的任何建议将不胜感激。提前致谢!
更新我查看了组织者线程 0 名称中的崩溃日志:调度队列:com.apple.main-thread 线程 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 我已经注释掉了在 viewDidLoad / willDisappear 中开始/停止更新位置的两行。
不再崩溃,但为什么呢?