在SCNNode上加载WebView CALayer时出现问题

时间:2017-01-26 03:09:14

标签: ios uiwebview calayer scenekit

我正在寻找帮助,弄清楚如何解决这个问题。 SceneKit允许您将CALayer设置为SCNNode外观的内容。是否可以从UIWebView获取CALayer并将其呈现为SCNMaterial的内容?

我正在尝试将SCNNode的漫反射内容更改为函数中的WebView层 - (void)webViewDidFinishLoad:(UIWebView *)webView完成,但代码崩溃。

但我收到此错误:43282] bool _WebTryThreadLock(bool),0x7fae33f33680:试图从主线程或Web线程以外的线程获取Web锁。这可能是从辅助线程调用UIKit的结果。现在崩溃......

这是我的参考代码

//
//  GameViewController.h
//  WebViewLayerTest
//

#import <UIKit/UIKit.h>
#import <SceneKit/SceneKit.h>

@interface GameViewController : UIViewController <UIWebViewDelegate,SCNNodeRendererDelegate>
@property (nonatomic, retain) IBOutlet UIWebView *webView;
@property (nonatomic,retain) SCNBox *box;
-(void) storeLayer;
@end



//
//  GameViewController.m
//  WebViewLayerTest
//
//

#import "GameViewController.h"
#import <QuartzCore/QuartzCore.h>

@implementation GameViewController 

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.webView = [[UIWebView alloc] initWithFrame:CGRectMake(10, 10, self.view.bounds.size.width-20.0, self.view.bounds.size.height-30.0)];
    
    NSString *urlAddress = @"https://www.google.com/";
    NSURL *url = [NSURL URLWithString:urlAddress];
    NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
    [self.webView loadRequest:requestObj];
    
    self.webView.delegate = self;

    // create a new scene

    SCNScene *scene = [SCNScene scene];

    // create and add a camera to the scene
    SCNNode *cameraNode = [SCNNode node];
    cameraNode.camera = [SCNCamera camera];
    [scene.rootNode addChildNode:cameraNode];
 
    //    // place the camera
    cameraNode.position = SCNVector3Make(0, 0, 15);

    // create and add a light to the scene
    SCNNode *lightNode = [SCNNode node];
    lightNode.light = [SCNLight light];
    lightNode.light.type = SCNLightTypeOmni;
    lightNode.position = SCNVector3Make(0, 10, 10);
    [scene.rootNode addChildNode:lightNode];
  
    // create and add an ambient light to the scene
    SCNNode *ambientLightNode = [SCNNode node];
    ambientLightNode.light = [SCNLight light];
    ambientLightNode.light.type = SCNLightTypeAmbient;
    ambientLightNode.light.color = [UIColor darkGrayColor];
    [scene.rootNode addChildNode:ambientLightNode];
   
    
    self.box = [SCNBox boxWithWidth:1.0 height:1.0 length:1.0 chamferRadius:0.05];
    self.box.firstMaterial.diffuse.contents = [UIColor blueColor];
    
    SCNNode* ship = [SCNNode nodeWithGeometry:self.box];
    //    // animate the 3d object
    [ship runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:2 z:0 duration:1]]];
    //
    //    // retrieve the SCNView
    SCNView *scnView = (SCNView *)self.view;
    //
    
    [scene.rootNode addChildNode:ship];
    //    // set the scene to the view
    scnView.scene = scene;
    
    // allows the user to manipulate the camera
    scnView.allowsCameraControl = YES;
    
    // show statistics such as fps and timing information
    scnView.showsStatistics = YES;
    
    // configure the view
    scnView.backgroundColor = [UIColor blackColor];
    
    
   
    
    // add a tap gesture recognizer
//    UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)];
//    NSMutableArray *gestureRecognizers = [NSMutableArray array];
//    [gestureRecognizers addObject:tapGesture];
//    [gestureRecognizers addObjectsFromArray:scnView.gestureRecognizers];
//    scnView.gestureRecognizers = gestureRecognizers;
}

- (void) handleTap:(UIGestureRecognizer*)gestureRecognize
{
    // retrieve the SCNView
    SCNView *scnView = (SCNView *)self.view;
    
    // check what nodes are tapped
    CGPoint p = [gestureRecognize locationInView:scnView];
    NSArray *hitResults = [scnView hitTest:p options:nil];
    
    // check that we clicked on at least one object
    if([hitResults count] > 0){
        // retrieved the first clicked object
        SCNHitTestResult *result = [hitResults objectAtIndex:0];
        
        // get its material
        SCNMaterial *material = result.node.geometry.firstMaterial;
        
        // highlight it
        [SCNTransaction begin];
        [SCNTransaction setAnimationDuration:0.5];
        
        // on completion - unhighlight
        [SCNTransaction setCompletionBlock:^{
            [SCNTransaction begin];
            [SCNTransaction setAnimationDuration:0.5];
            
            material.emission.contents = [UIColor blackColor];
            
            [SCNTransaction commit];
        }];
        
        material.emission.contents = [UIColor redColor];
        
        [SCNTransaction commit];
    }
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {
    
    NSLog(@"Finished Loading");
    
//    self.box.firstMaterial.diffuse.contents = _webView.layer;

    dispatch_async(dispatch_get_main_queue(),^ {
        self.box.firstMaterial.diffuse.contents = _webView.layer;
    } );
//    [self performSelectorOnMainThread:@selector(storeLayer) withObject:nil waitUntilDone:YES];

}

-(void) storeLayer{
    self.box.firstMaterial.diffuse.contents = _webView.layer;
}

- (BOOL)shouldAutorotate
{
    return YES;
}

- (BOOL)prefersStatusBarHidden {
    return YES;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
        return UIInterfaceOrientationMaskAllButUpsideDown;
    } else {
        return UIInterfaceOrientationMaskAll;
    }
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Release any cached data, images, etc that aren't in use.
}

@end

1 个答案:

答案 0 :(得分:2)

我遇到了同样的问题,我通过以下方法解决了。

<强> 1。实验发现CALayer可以直接用于设置diffuse.contents,但是UIView不能

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        guard let imageAnchor = anchor as? ARImageAnchor else { return }
        let testLayer = CALayer()
        testLayer.frame = self.view.bounds
        testLayer.backgroundColor = UIColor.blue.cgColor

        let referenceImage = imageAnchor.referenceImage
        let plane = SCNPlane(width: referenceImage.physicalSize.width,
                             height: referenceImage.physicalSize.height)
        plane.firstMaterial?.diffuse.contents = testLayer
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = -.pi / 2

        node.addChildNode(planeNode)
    }

}

<强> 2。在webview完成加载后获取webview快照。

func captureWebview() -> UIImage{
    var image : UIImage!
    UIGraphicsBeginImageContextWithOptions(webView.scrollView.contentSize, webView.scrollView.isOpaque, 0.0)
    let savedContentOffset = webView.scrollView.contentOffset
    let savedFrame = webView.scrollView.frame
    webView.scrollView.contentOffset = CGPoint.zero
    webView.scrollView.frame = CGRect(x: 0, y: 0, width: webView.scrollView.contentSize.width,height: webView.scrollView.contentSize.height)

    webView.scrollView.layer.render(in: UIGraphicsGetCurrentContext()!)
    image = UIGraphicsGetImageFromCurrentImageContext()

    webView.scrollView.contentOffset = savedContentOffset
    webView.scrollView.frame = savedFrame

    UIGraphicsEndImageContext()

    return image
}

第3。将webview快照设置为diffuse.contents

func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
     guard let imageAnchor = anchor as? ARImageAnchor else { return }

        let referenceImage = imageAnchor.referenceImage
        let plane = SCNPlane(width: referenceImage.physicalSize.width,
                             height: referenceImage.physicalSize.height)
        plane.firstMaterial?.diffuse.contents = self.testImage
        let planeNode = SCNNode(geometry: plane)
        planeNode.eulerAngles.x = -.pi / 2

        node.addChildNode(planeNode)


}