I'm developing some kind of card game in which my view objects (cards, etc) are all CALayers. These objects are added as sublayers of a mainBoard (a CALayer), which, in turn, is a sublayer of the [UIView layer] property of my mainView. Everything is Ok, I can do hit testing to verify which layer is being touched through the touchesBegan:withEvent: callback of the mainView class normally, and so on.
However, I need to have a scroll inside my mainBoard to show "bigger" CALayers, so I tried first adding the "bigger" CALayer inside a CAScrollLayer, but I noticed that the CAScrollLayer doesn't really scroll (doesn't handle user input, neither draws scrollbars). So, the workaround would be to add an UIScrollView directly to the mainView UIView. The UIScrollView scrolls perfectly, until I try to add the "bigger" layer to it, by using [scrollView.layer addSublayer:<bigger layer>].
Does anyone has any idea of how I can have "scrollable" objects inside a CALayer?
Thanks!
nacho4d pointed the answer for my question.
Replying to mark: the "bigger" layer (a layer whose bounds is bigger than the area available in the screen (and so, needs to be contained in a scrollable area)).
The solution was to first wrap the "bigger" layer in a UIView:
UIView *wrapperView = ...
[wrapperView.layer addSublayer:<bigger_layer>];
afterwards, I can add the view as a subview of the UIScrollview:
[<uiscrollview> addSubview:wrapperView];
// set up scrolling properties
<uiscrollview>.contentSize = <bigger_layer>.frame.size;
The problem was that I was trying to add the "bigger" layer as a sublayer of the UIScrollview directly, instead, wrapping it in a UIView and adding it as a subview worked.
Thanks
Related
I have a CATiledLayer within a UIView and the UIView also contains a subview.
How can I make sure that the subview is always drawn above the layer?
Most of the time I get the tile layer covering the subview.
By default all layers (hence views) added in the last are drawn on the top. You can change the default with -insertSublayer:below: and similar methods:
[view.layer insertSublayer:tiledLayer below:thatSubview.layer]
I have a UIView which contains a zoomable UIImageView and also another semitransparent UIView on top of that.
What I am trying to achieve is to be able to zoom the UIImageView while keeping the semitransparent view static and not zoomed.
If I add the semitransparent UIView on top of the UIImageView (which is added to the UIScrollView), everything zooms. However, if I add both as subviews to the base UIView, the touches only get tracked is the semitransparent UIView since its the last one added.
I do need control to reside first at the semitransparent UIView for the touches since I may want to resize the semitransparent view. However, I'd like to pass control of the touches to the UIScrollView if two fingers are used. Is there anyway for me to achieve this? The nextresponder doesn't seem to work. I also tried to use hittest in addition to subclassing UIWindow, but the base UIView needs to push/pop navigation controlling ability so I don't think I can subclass UIWindow to push onto the navigation stack.
Any help would be appreciated.
Thanks,
Winston
Hm.. you can try this hierarchy (possibly subclasses):
UIView (container)
> UIView (semitransparent overlay)
> UIScrollview
- UIView (zoomable content)
Like this, the overlay does not scale.
The tricky thing then is the user interaction on multiple layers. Its easy if there are areas in your overlay that should not detect user touches, for that you just set the UIView property 'userInteractionEnabled' to 'NO' for the view parts where touches should be 'forwarded' to the underlaying layers.
But if I get you right, you need something more complicated. You probably could set up some kind of master-touch-controller in the container UIView, that finds out what is happening and then calls certain methods of its subviews / forwards the events.
I don't know all the exact methods you need to override/implement in the container, but check out the tapZoom demo from the ScrollView Suite sample code. It's a pretty nice example there.
Just out of curiosity, may I ask what this interaction model is used for?
I want to have a UIView subclass that has a border image, but I don't want or care about this 'new' frame/bounds around the border image itself.
What I wanted to do was just use drawRect and draw outside of the rect but all drawing is clipped and I don't see a way to not clip drawing outside of this context rect.
So now I have added a sublayer to the views layer, set [self clipsToBounds] on the view and override setFrame to control my sublayers frame and always keep it at the proper size (spilling over the views frame by 40px).
The problem with this is that setFrame on a uiview by default has no animation but seTFrame on a calayer does.
I cant just disable the animations on the calayers setFrame because if I were to call setFrame on the uiview inside a uiview animation block the calayer would still have its animation disabled.
The obvious solution is to look up the current animationDuration on the uiview animation and set a matching animation on the sublayer, but I don't know if this value is available. And even if it is, I'm afraid that calling an animation from within another animation is wrong.
Unfortunately the best solution is to not use a calayer at all and just add a uiview as a subview and draw into that just like I am drawing into my layer, and hope that with autoresizingMask set to height and width that everything will 'just work'. Just seems like unnecessary overhead for such a simple task.
My solution would be to override the initWithFrame: to add the surrounding border pixels and contain the content in a subview. It probably is unneccesary overhead but definietly the "cocoa" route. It's probably going to be easier in the end too since a subview structure will allow you to edit the content seperatly from the border so you dont have to redraw the border when you redraw the content. And keeping them seperate simply makes sense from a OOP perspective.
The clipsToBounds route is probably the easiest route besides the subview structure but managing the border and content in one drawing cycle and in one object will probably be a lot more work so it'll be worth the overhead.
Excuse any typos, typed this from my iPhone.
I have a custom UIView that is composed entirely of CALayers.
In the awakeFromNib method it creates and sets all the CALayers into their appropriate positions (CAGradientLayer, several CATextLayers, and a few custom CALayer subclasses). The custom UIView does not override the drawRect: method because there's no drawing done directly into the view (all of the drawing is done in the sublayers).
So I took this view and embedded it in a UIScrollView. The problem? No scroll bars appear and the view does not scroll. The view is clearly larger than the bounds of the scroll view, and instead of allowing me to scroll, it just cuts off at the scroll view bounds.
What could be wrong here?
You have to set the scrollView's contentSize.
My setup is a UIScrollView in the center of the screen (on the iPhone - like 300x400 positioned in the center) that contains a UIView of the same width, so it scrolls it vertically. In this UIView i draw custom subviews with labels etc (it's a scoreboard with various colors).
What i'd like to have is some shadow below my UIScrollView, so that the whole scrolling scoreboard floats over my background.
I have found this nice post
How do I draw a shadow under a UIView?
I use this code in my ScrollView subclass but it doesn't work for me. Maybe because I don't draw the actual shapes in the ScrollView's drawRect: (since they are drawn on the UIView).
Also I guess that in order to have the View scroll in the ScrollView and the shadow of the ScrollView outside the scrolling area, I guess I should extend the "bounds" of the ScrollView, right?
It's not quite clear to me what you're asking but, if you want the scrollView contents to scroll over a static image you simply need to add a UIView (or more likely a UIImageView) to your superview and then add your UIScrollView to that. If you set he background colour of the UIScrollView to be celarColor, the background image will show through - so you have a view heirarchy like:
UIWindow
UIView <----- your background here
UIScrollView
Scrolling subviews <----- high score table here
If you draw your highscore table in the scrolling subviews using CoreGraphics, the answer in the question you linked to will also work.
How about explicitly filling the entire self.bounds rectangle in your scroll view subclass' drawRect: method before calling super?
Another idea is to put the scroll view inside of another view which does the shadow drawing.