So I'm pretty new to AutoLayout, but more often than not I'm able to hack my views into shape or model off samples on the web.
However, I've created this rather complex view that just doesn't resize no matter what constraints I try.
Here are a few screenshots of what's going on.
The first shot is my Interface Builder layout. It's got a 4-corners kind of thing going on, with a UIImageView in each corner. In the center is a blurred VisualEffectView; it lays on top of the images. The layout was constructed with the parent view at 200x200
The second shot is a successful rendering at 200x200. As you can see, the 4 images load fine (yeah, I know they're a bit stretched, I just haven't handled their scaling code yet). Programmatically, I set the cornerRadius properties of both the parent view and the blurred view to 1/2 their width, so as to make them circular. Also programmatically, I added a label as a subview to the blurred view.
Then it all goes downhill. The third shot is my attempting to render the view at 250x250. The parent view renders well and maintains a circular shape, but just about everything else is wrong.
The most frustrating part is the UIImageViews, which all go haywire and extend their bounds even though I've set them to be equal widths.
The blurred view at least stays centered, but something isn't called which prevents its bounds.width property to be updated, which is what the cornerRadius is based off of.
The label doesn't stay center in the blurred view, despite setting its autoesizingMask to flexible all around.
Here is a snippet of my initialization code, which might be useful.
Any help that you all could provide would be greatly appreciated (even if it just fixes one of the several issues).
P.S. I apologize for the cats pics.
Edit: I achieved the desired result by writing the code manually and ditching Interface Builder and AutoLayout entirely.
Related
I have an iPad app similar to the iPad Keynote with a narrow overview on the left and a paged UICollectionView of my "slides" on the right. The collection view is using the default FlowLayout. Some of these slides are standard PDFs and some are embedded UIViewControllers that have been scaled (with a CGAffineTransform) and embedded in the cell. I'd like to smoothly animate the overview sidebar offscreen and zoom the current page cell to fullscreen. The collection view should allow paged swiping at whatever size. I'm using storyboards and autolayout.
I think I need to simultaneously animate about three things:
The collection view constraints (to the sidebar) to enlarge/shrink it
The flow layout's sizeForItemAt: value
The CGAffineTransform on the embedded view controller.
I have some pieces working (a single embedded View Controller "slide" that scales correctly) but cannot get the collectionView/cell resize dance to work correctly. The cell resize animation is jerky, or ends up with the wrong offset, or works for the leftmost cell but not for other cells.
I've tried most of the suggestions in the answers to this question but with no convincing success. I can't believe it's impossible but at this point I'm considering the smoke and mirrors approach of animating a static slide and hiding it after the animation completes. The attached video - ignoring the glitches - illustrates the kind of effect I'm after:
It's worth noting, on close inspection, that Keynote cheats somewhat when it comes to swiping between slides in edit mode, and manually manages the next slide sliding onscreen, so probably doesn't use a UICollectionView.
Has anyone done anything similar, or have any suggestions for things to try?
I managed to solve this. There's a proof-of-concept GitHub repo here.
There are a few moving parts:
There are two pieces of UIView scaling code. I suspect these could be combined with suitable delegate references as the nested view controllers are embedded. The first piece scales (statically) correctly when the Collection View cell is created. The second is an animated scale/translate when the sidebar size is changed.
I added a FlowLayout subclass to remove flicker as the collection's layout is invalidated during scaling.
The sidebar-related transform in the top-level VC uses most of the tricks in the book - invalidateLayout(), performBatchUpdates(...), layoutIfNeeded() etc. as well as animating the contentOffset to the correct value. Some manual tracking of the correct page/slide is required.
There's still a slight flicker occasionally at the start of resizing. This may not be an issue with my particular colour-scheme which will be black on dark gray. Bonus points if anyone can suggest how to track this down or alleviate it.
My goal was to make a View of buttons and have it grow with screen size so it works on all devices, filling 90% of the width of the screen. I am not using constraints as it is an older program, and turning on constraints would be too big a job right now. I was hoping to "cheat" my way around this. So, I tried this:
I have a smaller View created inside a full screen View (created in interface builder) that is basically a standard "view" item. Inside that smaller View are a bunch of UIButtons that make my "keyboard."
I would like to scale the smaller view (which visually scales the buttons too), and still have the buttons respond properly as they change size. So I have done this:
Setup:
View (buttonView) with UIButtons inside that view. The view and the buttons are attached (in Interface Builder) to IBOutlets and IBActions as necessary. The following code scales the view to match the screen:
_keyboardCard.transform = CGAffineTransformIdentity; // reset to normal
CGFloat keyboardWidth = _keyboardCard.bounds.size.width;
// screenWidth is set earlier and has width bounds value for full screen
CGFloat widthScale = (screenWidth/keyboardWidth)*0.9; // 90% of full screen width
_keyboardCard.transform = CGAffineTransformScale(CGAffineTransformIdentity, widthScale, widthScale);
This actually works visually just fine, and the "keyboardCard" grows in size, as do the buttons inside (visually), just like I expected. It looks good as I change from device to device.
But there are times when some of the buttons stop responding. SOME continue to work just fine, especially near the center, while others just stop. The touch failures start on ALL the edges, and work their way inward as the size grows. It is almost as if there is a "mask" stopping the buttons from working on the edges.
So the question is: Is this a legitimate ways to do this? It does not seem to be documented anywhere. And if not, what does one suggest as a way to make a view with buttons scale up and down and still have the buttons work as expected?
Or perhaps I am just better off making a small, medium, and large View of the "keyboard", and just pick the closest one based on screen size?
Suggestions and comments please. Thank you.
I have a UIVisualEffectView with a UIBlurEffect effect as a pinned UICollectionViewCell in a UICollectionView.
The blur is masked with an icon so that the icon appears in a subtle shimmery way, responding to whatever it is drawn on top of.
The effect is on top of the collection's background. As it stays pinned and the varying background scrolls underneath, it gently changes to reflect the background and looks very nice.
However, except for the collections's scrolling background, most content is drawn in front of the blur effect. This is necessary because that other content is more important and shouldn't be obscured.
Even though it is behind the more important content, I'd like the effect view to reflect the content that is scrolling in front of it. Any suggestions for how this might be made to work?
Ideas:
Could I grab a chunk from the previously drawn frame and draw this under the effect view? How would I do that?
Could I render the whole collection view, apply the blur, and then render everything that should be on top of the blur a second time? How can I make that efficient?!
Thanks.
I went with a slightly refined version of the second option.
I added some duplicate cells to the collection view that are rendered behind the blur, and then also in front. I've only done this for one of my cell types with lots of colour in it. It works pretty well.
If you have a better approach I'll happily assign you the correct answer.
I have a UIScrollView over an image at the bottom of my app that acts as a dock with icons that can be scrolled through horizontally. Instead of the harsh edges of the UIScrollView, I would like the icons to fade out for a more aesthetically pleasing look. Being new to iOS development, I don't know if either of these would be valid options:
Create a faded image to use as an overlay on the scrollview so the
icons only appear through the visible portion.
Actually change the
alpha of the images based on their distance from the center (or from
each edge).
I suspect the first idea would be the most simple, but I'd like to throw this out there for any other ideas.
Note: I did see this tutorial, however that technique assumes that the background is a solid color. If I were to do it programatically, I would probably need to fade the individual images.
You can definitely implement something along the lines of #2. It'd be something similar to what the tutorial describes. The alpha transition however won't be as smooth as using the gradient layer mentioned in the tutorial or using an image since the entire icon would have the same alpha. How much discernible the difference is depends on the size of your icons. Smaller icons, very few will be able to tell the difference. Larger icons the difference would be quite clear.
You'd have to implement the
(void)scrollViewDidScroll:(UIScrollView *)scrollView
method in your scroll view's delegate class. This method will get called every time the scroll view changes the location of its content. In this method you can call its subviews and adjust their alphas as required. To optimize it a bit instead of calling the alpha adjustments on all the elements you can just update the subviews which are still partially/completely visible.
EDIT: to figure out which views to adjust you'll use the contentOffset property of the scrollView that gets passed as a parameter in the above method.
After reading about UIView's autoresizingMask on SO and developer.apple.com I'm still unclear what the purpose is. What's a situation where setting this property is necessary?
Yes, it is often necessary to set it if you don't want to resize the views manually. Note that it is mostly useful for subviews (i.e. those views that don't take the whole screen) rather then the main view of your app.
Views typically may need resizing if:
the device is rotated
an extra view (say, an ad) is added to the view, so the existing subviews have less available space.
For example, suppose if you have a view with two buttons on it, one in the top-left corner, another in the top-right corner. In order for the buttons to get wider when the view transitions from portrait to landscape, you need to set the FlexibleLeftMargin to the right button, FlexibleRightMargin to the left button.
Edit: autoresizingMask is also the first thing to look at if you see weird holes or overlaps when device is rotated or a new subview is added. Quite often the proper setting of these masks for subviews can get you a nice looking view in both orientations without having to lay out subviews manually - but usually it takes some experimenting.
Edit2: (since this is still gathering upvotes) Autoresizing masks are now mostly superseded with "Auto Layout", which allows for much more flexible constraints on views' sizes and positions. That being said, translatesAutoresizingMaskIntoConstraints is still occasionally useful for dynamically added views.
The purpose is that UIView properly shifts and resizes when its superview changes due to resizing, orientation change, showing editing controls in tableview cells etc.