facebookarchive / AsyncDisplayKit

Smooth asynchronous user interfaces for iOS apps.
http://asyncdisplaykit.org
Other
13.4k stars 2.2k forks source link

[Question] GMSPanoramaView and initWithViewBlock: #2013

Closed raphaels17 closed 7 years ago

raphaels17 commented 8 years ago

Hello

I tried some simple projects and now I try to spice thing up a bit. My project is a collectionNode where I see image of pokemon on a given location if any was taken else it shows the street view. I have added a spinner and a default image in case in a viewblock as per documentation, however I got very buggy behavior

1/the default street location is nowhere to be found. 2/ after sometime of scrolling up/down the view pops and the cache gets change by the pikachu ( as per screenshot).

Thank for your precious work and support!

screen shot 2016-07-30 at 10 56 29 pm
import UIKit
import AsyncDisplayKit
import GoogleMaps

class PokemonStopsCollectionViewCellNode: ASCellNode{

    let loadingIndicator = SpinnerNode()
    var imageNode = ASNetworkImageNode()
    var backUp : StreetViewNode?
    let pokemonSeen: PokemonSeen
 init(pokemonSeen: PokemonSeen) {
        self.pokemonSeen = pokemonSeen
        super.init()
        configure()
    }

    func configure() {
        addSubnode(loadingIndicator)
        configureImageNode()
    }
func configureImageNode() {
        let manager = PokemonsDataManager.sharedManager
        imageNode = ASNetworkImageNode(cache: manager, downloader: manager) //I suspect some wrong here, cause the cache is often incorrect when I scroll up and down and this takes forever.
        imageNode.contentMode = .ScaleAspectFill
        imageNode.delegate = self
        imageNode.URL = NSURL(string: pokemonSeen.url)
        imageNode.clipsToBounds = true
        imageNode.layerBacked = true

        addSubnode(imageNode)
    }
    override func layoutSpecThatFits(constrainedSize: ASSizeRange) -> ASLayoutSpec {
        let imageLayoutSpec = ASRatioLayoutSpec(ratio: 1, child: imageNode)
        let loadingLayoutSpec = ASRatioLayoutSpec(ratio: 1, child: loadingIndicator)
        var mainStackLayoutSpec = ASBackgroundLayoutSpec(child: imageLayoutSpec, background: loadingLayoutSpec)
        if backUp != nil { 
            let backUpLayoutSpec = ASRatioLayoutSpec(ratio: 1, child: backUp!)
            mainStackLayoutSpec = ASBackgroundLayoutSpec(child: backUpLayoutSpec, background: mainStackLayoutSpec)
        }

         mainStackLayoutSpec.flexGrow = true
         mainStackLayoutSpec.alignSelf = .Stretch
         mainStackLayoutSpec.sizeRange = ASRelativeSizeRange(
            min: ASRelativeSize(
                width: ASRelativeDimension(type: .Points, value: 250),
                height: ASRelativeDimension(type: .Points, value: constrainedSize.max.width * 0.75)
            ),
            max: ASRelativeSize(
                width: ASRelativeDimension(type: .Points, value: constrainedSize.max.width * 0.85),
                height: ASRelativeDimension(type: .Points, value: constrainedSize.max.width * 0.75)
            )
        )

        let mainStaticLayoutSpec = ASStaticLayoutSpec(children: [mainStackLayoutSpec])
        return mainStaticLayoutSpec 
    }

}
extension PartnerCollectionViewCellNode: ASNetworkImageNodeDelegate {

    func imageNode(imageNode: ASNetworkImageNode, didLoadImage image: UIImage) {
        loadingIndicator.removeFromSupernode()
    }
    func imageNode(imageNode: ASNetworkImageNode, didFailWithError error: NSError) {
        backUp =  StreetViewNode(partner: partner)
       addSubnode(backUp)
        setNeedsLayout() //  I don't see the street loading really

    }

}
class SpinnerNode: ASDisplayNode {
    var activityIndicatorView: UIActivityIndicatorView {
        return view as! UIActivityIndicatorView
    }

    override init() {
        super.init(viewBlock: { UIActivityIndicatorView(activityIndicatorStyle: .Gray) }, didLoadBlock: nil)
        preferredFrameSize.height = 32
    }

    override func didLoad() {
        super.didLoad()
        activityIndicatorView.startAnimating()
    }
}
class StreetViewNode: ASDisplayNode,GMSPanoramaViewDelegate  {
    var panoramaNear: CLLocationCoordinate2D?
    var StreetView: GMSPanoramaView?

     init(partner: Partner) {
        super.init(viewBlock: { GMSPanoramaView(frame: CGRectZero) }, didLoadBlock: nil)
        panoramaNear = CLLocationCoordinate2DMake(partner.latitude!, partner.longitude!)
        self.contentMode = .ScaleAspectFill
        StreetView = GMSPanoramaView.panoramaWithFrame(CGRectZero,nearCoordinate:panoramaNear!)
        StreetView!.translatesAutoresizingMaskIntoConstraints = false
        StreetView!.delegate = self
        StreetView!.zoomGestures = false
        StreetView!.streetNamesHidden = true
        StreetView!.navigationGestures = false
        StreetView!.navigationLinksHidden = true
        StreetView!.orientationGestures = false
        preferredFrameSize.height = 200 // I know this is not good but I have idea what to do 
        preferredFrameSize.width = 200
    }

    override func didLoad() {
        super.didLoad()

    }
}
hannahmbanana commented 8 years ago

@raphaels17: thank you for clearly describing the issue and providing code.

Just like you put the GMSPanoramaView in a viewBlock, try moving all the GMSPanoramaView setup code into that block as well.

If that doesn't work, could you go one step further and make a sample project for us? It would make it faster for us to help you. thanks!

raphaels17 commented 8 years ago

50% complete The part 1 works like a charm ! the code below for future references.

However Caching not so much.. I only made minor improvement. I have 2 hurdles : 1 / I can't figure know how to capture my newly created view in viewblock, in particular ounce it has bounds to manipulate it didLoad or other methods. Am I tackling this correctly 2/So I kept my var StreetView out of the viewBlock, created another GMSPanorama and using didLoad method, I was able to cache it. however that is before the view is actually render, ie I have again a grey screen. I tried displayDidFinish but it looks like it never gets called. would you see a more appropriate methods?

class StreetViewNode: ASDisplayNode,GMSPanoramaViewDelegate  {
    var pokemon:Pokemon?
     init(pokemon: Pokemon) {
        super.init(viewBlock: {
            let streetView = GMSPanoramaView.panoramaWithFrame(CGRectZero,nearCoordinate:CLLocationCoordinate2DMake(pokemon.latitude!, pokemon.longitude!))
            streetView.translatesAutoresizingMaskIntoConstraints = false
            return streetView  }, didLoadBlock: nil)
        self.StreetView?.delegate = self
// this was a test  to see if I could save some using var pokemon
        self.StreetView = GMSPanoramaView.panoramaWithFrame(CGRectMake(0,0,400, 300),nearCoordinate:CLLocationCoordinate2DMake(pokemon.latitude!, pokemon.longitude!))
        self.contentMode = .ScaleAspectFill
        self.pokemon = pokemon
    }

    override func didLoad() {
        super.didLoad()
        debugPrint(self.view.bounds) // this is null
       //  backup plan snapshot of the Street view
        let snapShot = UIImage(view:  StreetView!)
        //MARK Caching --> I get  a grey screen
        let _ = cacheImage(snapShot, pokemonId: "\(self.pokemon!.id)")
    }
}
hannahmbanana commented 8 years ago

@raphaels17: Would it be possible to provide a sample app showing the issue? It would be much faster for me to help you.

I am having a bit of trouble understanding your question. (1) Your node may be being initialized on a background thread, so it is important to keep -init thread safe - no creation / accessing of UIViews or CALayers. (2) You may set properties on the view in didLoad (called on the main thread), and you should (3) do main thread layout in -layout

Here are some notes on ASDisplayNode subclass override methods:

subclass method notes
-init Node initialization occurs on background thread. This has only one key requirement for the developer: never create or touch any UIKit objects in -init. This includes the node's own .view / .layer. Do not access these (e.g. self.view.hidden or addGestureRecognizer:) Instead, use the node properties if available (e.g. self.hidden), or do these things in -didLoad.
-didLoad (a.k.a. the node's .view "did load") This method is conceptually similar to UIViewController's -viewDidLoad method and is the point where the node's view has been loaded. It's the first chance to safely access the node's view or layer. It is guaranteed to be called on the main thread and is the correct place to do any direct UIKit setup.
-layoutSpecThatFits: This method defines the layout and does the heavy calculation on a background thread. The layout spec object has the important responsibility of calculating the size and position of all subnodes, and also the overall size of the node. Because it is run on a background thread, you should not set any UIView or CALayer properties directly, but you can use the node equivalent properties. Also, do not add or remove subnodes in this method.
-layout This method is always called on the main thread, and should always begin with [super layout]. It is called in the same way as -[UIView layoutSubviews]. This is a good spot to set view-based properties that change based on your node state, like hidden or backgroundColor. Less than 1 in 10 subclasses will need this.

Longer version with more information: http://asyncdisplaykit.org/docs/subclassing.html

hannahmbanana commented 8 years ago

@raphaels17: Were you able to figure out the 2nd part to your question?

raphaels17 commented 8 years ago

Hi

Not yet. I was on a little summer break :-) I should have been more diligent and let you know. I'll put a project this friday.

2016-08-09 8:18 GMT+02:00 Hannah Troisi notifications@github.com:

@raphaels17 https://github.com/raphaels17: Were you able to figure out the 2nd part to your question?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/AsyncDisplayKit/issues/2013#issuecomment-238463678, or mute the thread https://github.com/notifications/unsubscribe-auth/ADghiMNOOGEt0c9tQ7inh9ExMfUZjVc6ks5qeBvFgaJpZM4JY5Hm .

hannahmbanana commented 8 years ago

@raphaels17: vacations are important too. :) No rush on the sample project, but we are here to help whenever you do make it.

raphaels17 commented 8 years ago

Hello I am back !

and I have a done a little project for my use case. I have slightly modified it to simplify it, but no worry you still get to see pokemons everywhere.

My issue is I try to use ASDK in conjunction with google streetView, which resort to UIKit and the Main thread. How can get both to work? (plus how come a delegate use can be limited to be used on the main thread)

https://github.com/raphaels17/ASDK.test/blob/master/ASDK.test/PokemonCollectionCellNode.swift

Thanks in advance for your precious help

raphaels17 commented 8 years ago

@hannahmbanana have you had a chance to see how I could hack it. I know init with view block prevent I use delegate method, is there any way around?

Adlai-Holler commented 7 years ago

Hi @raphaels17 I've modified the cell node's code. Sorry for the delay!

Here you are! https://gist.github.com/Adlai-Holler/ee6690471717257f0b3a07b06f399fbe

If it's still not working, please reopen or visit our Slack where there's tons of helpful people: http://asyncdisplaykit.org/slack