0

我已经在一个应用程序上工作了几个月,但最终遇到了一个我自己无法解决的问题,并且在互联网上找不到任何可以帮助的东西。

我在我的应用程序中使用了几个普通的 UIAlertViews。有些有 2 个按钮,有些有 3 个按钮,还有一些有 2 个按钮和一个文本字段。但是,所有人都有相同的问题。当你调用 [someAlertView show]; 警报视图显示正常,但突然它的图形上下文似乎已损坏,如您从屏幕截图中看到的那样。

UIAlertView 渲染不正确

这在 iPhone 和 iPad 模拟器(5.0 和 5.1)上都会发生,在 iPad 和 iPhone4S 设备上也会发生。

显示的图像是发生在 alertView 后面的任何内容。

警报仍然有效,我可以单击按钮,在文本字段中输入,然后当它关闭时,委托方法被正确调用,一切恢复正常。当 alertView 再次出现时,同样的事情发生了。

警报背后的视图是一个自定义 UIScrollView 子类,其内容大小约为 4000 x 1000 像素,以 UIImage 作为背景。png 文件大部分是透明的,因此内存大小只有 80kB 左右,手机在渲染它时没有问题 - 滚动视图仍然完全响应且不慢。作为子类的一部分,它还附加了一个 CADisplayLink 计时器。我曾尝试在显示 alertView 之前禁用它,但这没有任何区别,所以我怀疑这是问题所在。

这个应用程序是我为一个大学项目制作的一个应用程序的部分重写,它可以在相同大小和子类的滚动视图的顶部显示 UIAlertViews 而不会出现问题。这个应用程序和那个应用程序之间的区别在于,在我的旧应用程序中,我将 UIAlertView 子类化以添加额外的东西,例如pickerView,但是我决定我不喜欢它的外观,所以将所有内容都从警报中移出只是坚持使用标准的 UIAlertView。

截图中的 alertView 是这样调用的:

- (IBAction)loadSimulation:(id)sender {
    importAlert = [[UIAlertView alloc] initWithTitle:@"Load Simulation" message:@"Enter Filename:" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"Load", nil];
    [importAlert setAlertViewStyle:UIAlertViewStylePlainTextInput];
    [importAlert showPausingSimulation:self.simulationView]; //Calling [importAlert show]; makes no difference.
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        [self hideOrganiser]; //Not an issue as the problem occurs on iPad as well.
    }
}

这是分类的 AlertView 以添加停止 scrollViews CADisplay 链接的能力。

@interface UIAlertView(pauseDisplayLink)
- (void)showPausingSimulation:(UILogicSimulatorView*)simulationView;
@end

@implementation UIAlertView(pauseDisplayLink)

- (void)showPausingSimulation:(UILogicSimulatorView *)simulationView {
    [simulationView stopRunning];
    [simulationView removeDisplayLink]; //displayLink needs to be removed from the run loop, otherwise it will keep going in the background and get corrupted.
    [self show];
}

发生这种情况时,我没有收到任何内存警告,所以我怀疑这是由于缺乏资源。

有没有人遇到过这样的问题?如果您需要更多信息,我可以尝试提供,但我可以发布的代码有限。任何帮助将不胜感激,我已经尝试解决这个问题两周但无法解决。


编辑:看起来它根本不是 AlertView(或者更确切地说它不仅仅是 alertView),因为当我删除它后面的滚动视图时问题就消失了,所以两者之间一定存在一些问题。这是我的 UIScrollView 子类的代码:.h 文件:#import #import

@class ECSimulatorController;
@interface UILogicSimulatorView : UIScrollView {
    CADisplayLink *displayLink;
    NSInteger _updateRate;
    ECSimulatorController* _hostName;
}

@property (nonatomic) NSInteger updateRate;
@property (nonatomic, strong) ECSimulatorController* hostName;

- (void) removeDisplayLink;
- (void) reAddDisplayLink;

- (void) displayUpdated:(CADisplayLink*)timer;
- (void) startRunning;
- (void) stopRunning;
- (void) refreshRate:(NSInteger)rate;

- (void) setHost:(id)host;

- (void)setMinimumNumberOfTouches:(NSInteger)touches;
- (void)setMaximumNumberOfTouches:(NSInteger)touches;

@end

.m 文件:

#import "UILogicSimulatorView.h"
#import "ECSimulatorController.h"
#import <QuartzCore/QuartzCore.h>

@implementation UILogicSimulatorView

@synthesize updateRate = _updateRate;
@synthesize hostName = _hostName;

- (void)reAddDisplayLink {
    [displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; //allows the display link to be re-added to the run loop after having been removed.
}

- (void)removeDisplayLink {
    [displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; //allows the display link to be removed from the Run loop without deleting it. Removing it is essential to prevent corruption between the games and the simulator as both use CADisplay link, and only one can be in the run loop at a given moment.
}

- (void)startRunning {
    [self refreshRate:self.updateRate];
    [displayLink setPaused:NO];
}

- (void)refreshRate:(NSInteger)rate {
    if (rate > 59) {
        rate = 59; //prevent the rate from being set too an undefined value.
    }
    NSInteger frameInterval = 60 - rate; //rate is the number of frames to skip. There are 60FPS, so this converts to frame interval.
    [displayLink setFrameInterval:frameInterval];
}

- (void)stopRunning {
    [displayLink setPaused:YES];
}

- (void)displayUpdated:(CADisplayLink*)timer {
    //call the function that the snakeController host needs to update
    [self.hostName updateStates];
}

- (void)setHost:(ECSimulatorController*)host;
{
    self.hostName = host; //Host allows the CADisplay link to call a selector in the object which created this one.
}

- (id)initWithFrame:(CGRect)frame
{
    //Locates the UIScrollView's gesture recogniser
    if(self = [super initWithFrame:frame])
    {
        [self setMinimumNumberOfTouches:2];
        displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayUpdated:)]; //CADisplayLink will update the logic gate states.
        self.updateRate = 1;
        [displayLink setPaused:YES];
    }
    return self;
}

- (void)setMinimumNumberOfTouches:(NSInteger)touches{
    for (UIGestureRecognizer *gestureRecognizer in [self gestureRecognizers])
    {
        if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
        {
            //Changes the minimum number of touches to 'touches'. This allows the UIPanGestureRecogniser in the object which created this one to work with one finger.
            [(UIPanGestureRecognizer*)gestureRecognizer setMinimumNumberOfTouches:touches];
        }
    }
}

- (void)setMaximumNumberOfTouches:(NSInteger)touches{
    for (UIGestureRecognizer *gestureRecognizer in [self gestureRecognizers])
    {
        if([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
        {
            //Changes the maximum number of touches to 'touches'. This allows the UIPanGestureRecogniser in the object which created this one to work with one finger.
            [(UIPanGestureRecognizer*)gestureRecognizer setMaximumNumberOfTouches:touches];
        }
    }
}

@end
4

2 回答 2

1

好吧,我已经设法解决了这个问题。真的,它可能只是掩盖问题而不是找到路线原因,但在这一点上,我会接受它。

首先是一些代码:

@interface UIView (ViewCapture)
- (UIImage*)captureView;
- (UIImage*)captureViewInRect:(CGRect)rect;
@end

@implementation UIView (ViewCapture)

- (UIImage*)captureView {
    return [self captureViewInRect:self.frame];
}

- (UIImage*)captureViewInRect:(CGRect)rect
{    
    UIGraphicsBeginImageContext(rect.size);  
    CGContextRef context = UIGraphicsGetCurrentContext();  
    [self.layer renderInContext:context];  
    UIImage *screenShot = UIGraphicsGetImageFromCurrentImageContext();  
    UIGraphicsEndImageContext();
    return screenShot;    
}
@end

- (void)showPausingSimulation:(UILogicSimulatorView *)simulationView {
    [simulationView stopRunning];
    UIView* superView = simulationView.superview;
    CGPoint oldOffset = simulationView.contentOffset;
    for (UIView* subview in simulationView.subviews) {
        //offset subviews so they appear when content offset is (0,0)
        CGRect frame = subview.frame;
        frame.origin.x -= oldOffset.x;
        frame.origin.y -= oldOffset.y;
        subview.frame = frame;
    }
    simulationView.contentOffset = CGPointZero; //set the offset to (0,0)
    UIImage* image = [simulationView captureView]; //Capture the frame of the scrollview
    simulationView.contentOffset = oldOffset; //restore the old offset
    for (UIView* subview in simulationView.subviews) {
        //Restore the original positions of the subviews
        CGRect frame = subview.frame;
        frame.origin.x += oldOffset.x;
        frame.origin.y += oldOffset.y;
        subview.frame = frame;
    }
    [simulationView setHidden:YES];
    UIImageView* imageView = [[UIImageView alloc] initWithFrame:simulationView.frame];
    [imageView setImage:image];
    [imageView setTag:999];
    [superView addSubview:imageView];
    [imageView setHidden:NO];
    superView = nil;
    imageView = nil;
    image = nil;
    [self show];
}

- (void)dismissUnpausingSimulation:(UILogicSimulatorView *)simulationView {
    UIView* superView = simulationView.superview;
    UIImageView* imageView = (UIImageView*)[superView viewWithTag:999];
    [imageView removeFromSuperview];
    imageView = nil;
    superView = nil;
    [simulationView setHidden:NO];
    [simulationView startRunning];
}

然后修改我的类中的解除委托方法以具有以下行:

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
    [alertView dismissUnpausingSimulation:self.simulationView];
    ...

当调用警报视图时,但在显示之前,我需要隐藏模拟器以防止它破坏警报。然而,仅仅隐藏它是丑陋的,因为所有可见的背后都是一个空视图。

为了解决这个问题,我首先从模拟器视图图形上下文中创建一个 UIImage。然后我创建一个与模拟器具有相同框架的 UIImageView 并将 UIImage 设置为其图像。然后我隐藏模拟器视图(解决警报问题),并将我的新 UIImageView 添加到模拟器超级视图。我还设置了图像视图的标签,以便以后可以找到它。

当警报消失时,图像视图然后根据其标签恢复,并从其父视图中删除。模拟器然后被取消隐藏。

结果是渲染问题消失了。

于 2012-07-30T10:04:23.170 回答
0

我知道现在回答这个问题为时已晚。最近我遇到了同样的问题。

我的案例: 向具有阴影效果的滚动视图添加了几个带有背景图像的自定义 UIView 和一些控件。我还设置了 shadowOffset。

解决方案: 经过一步一步的分析,我发现设置setShadowOpacity为我造成了渲染问题。当我评论那行代码时,它会将 UIAlertView 恢复为正常外观。

更多: 为了确保,我创建了一个新项目,使用 shadowOpacity 模仿原始 ui。但它并没有像我预期的那样导致渲染问题。所以我不确定根本原因。对我来说是setShadowOpacity

于 2012-09-18T09:00:04.517 回答