I have the following situation: I have a UIViewController, which has a UIScrollView inside of it. The content of this scroll view, is a "canvas", where things get drawn on using CoreGraphics. The image this canvas will ultimately show is similar to a tree structure, all done programatically. My problem is: I want to be able to increase the canvas size, so that the old image is on the middle of the new allocated canvas size, and this new canvas size should then be the scroll view content, for things to get drawn around at the new allocated border and panned with.
So far this is what I have:
In the canvas class:
if (totalWidthOccupiedByChildren > totalSpaceAvailableDad) {
CGSize newSize = CGSizeMake(self.frame.size.width*1.1, self.frame.size.height*1.1);
self.frame = CGRectMake(0, 0, newSize.width, newSize.height);
[self.delegate shouldIncreaseScrollView];
}
In the ViewController class:
-(void)shouldIncreaseScrollView{
self.scrollView.contentSize = CGSizeMake(paintV.frame.size.width, paintV.frame.size.height);
}
This works "ok", and both the canvas and scroll view are increased and now allow panning, but the old image shows at the upper left corner, and I need to center it. Any idea how?
What worked for me in the end was refactoring my architechture a bit, to that I had the variables I needed. But the property which I ended up setting is scrollView.contentOffset, just for future reference :)
What you can do is, putting all the stuff inside a UIView and adding that UIView to UIScrollView. You need to ensure that UIView.center equals UIScrollView.center.
Also, you will need to adjust UIView frame size along with UIScrollView size.
Related
I want just this simple effect:
that I have a picture automatically orients itself according to the device/screen orientation, it alway fill up the whole screen without stretching itself so that the aspect doesn't change.
I work with Xcode though I got a lot of answer with storyboard and UI builder,I really don't like such ways that work well but you have no idea how it works, so I was trying to find the way to do it programmatically instead.
I've tried to set a image view as subview, but it doesn't fix well after the device is rotated, it either stretch to fill the screen or move to somewhere else with size unchanged (when I trie to recalculate the frame and apply to the subview), then I tried to override the drawrect func, of the UIView, the frame works fine but the animation of rotating is broken (it stretches it self during the rotation and then skip into the final frame).
the perfect effect I want is just like iOS original photo app, when I check a photo, it resizes to fix the screen, and even when I rotate the screen, the animation works just perfect, the rotating and scaling happens in the same time and no skip of stretch happens. could anyone please give me some help?
THANKS!!!!!
the way I set the frame:
func locating (imgsize:CGSize,scrsize:CGSize) ->CGRect {
var size:CGSize
var ori:CGPoint
if ((imgsize.height / imgsize.width) * scrsize.width>=scrsize.height){
size = CGSize(width:scrsize.width,height:imgsize.height*(scrsize.width/imgsize.width))
ori = CGPoint(x:0,y:scrsize.height-imgsize.height*(scrsize.width/imgsize.width))
}
else {
size = CGSize(width:imgsize.width*(scrsize.height/imgsize.height),height:scrsize.height)
ori = CGPoint(x:-(imgsize.width*(scrsize.height/imgsize.height)-scrsize.width)/2,y:0)
}
return CGRect(origin:ori,size:size)
}
Apart from math's stuff! here is also the solutions that can work for you!
Set image width and height to superview width and height.
self.imageView.frame = self.imageView.superView?.bounds
Then set your imageView's contentMode property to aspectFill or aspectFit
self.imageView.contentMode = UIViewContentModeScaleAspectFit;
self.imageView.contentMode = UIViewContentModeScaleAspectFill;
How do you handle centering and scrolling content in a UIScrollView when the dimensions are both larger and smaller than the containing scroll view?
I have been trying to set values in layoutSubviews to handle the initial centering of the content, while still allowing for scrolling.
If the content is smaller in both dimensions, I can just set the frame and the image is properly centered for all rotations and orientations. Setting the contentInset will also work. contentOffset does not seem to work.
If the content is larger in both dimensions, I can set contentOffset for the initial display, and not modify it again to support scrolling.
What do I do if I have an image with one dimension larger, and the other smaller, than the scroll view?
contentOffset uses a CGPoint, and contentInset uses UIEdgeInsets (top, left, bottom, right). I have tried mixing positive and negative, since one dimension needs to be moved in and the other out, but haven't gotten anything to work.
My next thought is to resize the scroll view (and modify constraints I suppose) so that the content is never smaller than the container and use contentOffset.
I would really like to have a single approach that will work regardless of larger or smaller dimensions.
What is the best solution (a solution) to this problem?
After stepping back from this, getting a better understanding of UIScrollView, and rethinking, I have solved my problem.
For starters, layoutSubviews is the wrong way to go, at least for what I need.
Trying to resize the UIScrollView and update constraints seemed more trouble than it was worth.
For whatever reason, it didn't initially occur to me than I could use both contentOffset and contentInset at the same time (thought it was either/or for some reason), but that was my exact solution.
CGRect rectContent = CGRectMake(0, 0, image.size.width, image.size.height);
UIImageView *imageView = [[UIImageView alloc] initWithFrame:rectContent];
self.scrollView.contentSize = rectContent.size;
CGFloat fOffsetWidth = (rectContent.size.width < self.scrollView.bounds.size.width) ? (self.scrollView.bounds.size.width - rectContent.size.width)/2 : 0;
CGFloat fOffsetHeight = (rectContent.size.height < self.scrollView.bounds.size.height) ? (self.scrollView.bounds.size.height - rectContent.size.height)/2 : 0;
self.scrollView.contentInset = UIEdgeInsetsMake(fOffsetHeight, fOffsetWidth, fOffsetHeight, fOffsetWidth);
self.scrollView.contentOffset = CGPointMake((rectContent.size.width - self.scrollView.bounds.size.width)/2, (rectContent.size.height - self.scrollView.bounds.size.height)/2);
imageView.image = image;
[self.scrollView addSubview:imageView];
All image dimension possibilities (larger/smaller, one/both) are centered in the scroll view, and a larger image dimension is scrollable while a smaller dimension remains centered.
Perfect!
I'm using a UIPresentationController, but I can't sort out how to get it showing the way I want it to.
I'm editing the frameOfPresentedViewInContainerView() function, and I need to be returning the frame (a CGRect) to display my content in.
The tutorial I followed uses CGRectInsert(self.containerView.bounds, 50, 50), which makes the window centered with the borders brought in 50px. If I return self.containerView.bounds just as itself, the view takes up the whole screen.
I'd like the overlaying view to be the width of the parent view (so, self.containerView.bounds.width), but I want the height to be the size needed to show the content of the new view without cutting anything off.
I tried a CGRect at (0,0) with width and height from self.preferredContentSize, but it's not returning sizes that work.. What can I do?
I tried frame = CGRect(x:0, y: self.containerView.bounds.height/2, width: self.containerView.bounds.width, height: self.containerView.bounds.height/2) just as a test (but that's just making the view half the size of the parent view), but when I rotate the screen, suddenly the new view is almost off screen..
Aside from frameOfPresentedViewInContainerView(), I also had to add the following to make it work.
- (void)containerViewWillLayoutSubviews
{
self.presentedView.frame = [self frameOfPresentedViewInContainerView];
}
The scrollview shows a floorplan, and the UIView is a little green dot that shows the user's location on the floorplan. So I need to be able to zoom in, and have the green dot stay in the same spot on the floorplan. When I pan, the green dot moves correctly, but when I zoom, it moves away from where it should be.
Here is a screenshot of my storyboard. The small green square is the view that is a child of the Scrollview, and shows the user's location. I move it around programatically, but this is not causing the problem. http://i.imgur.com/TTRMXNS.png
Heres the code that puts the tilemap into the scrollview:
CCDirector* director = (CCDirector *)[self childViewControllers][0];
director.view.frame = CGRectMake(0, 0, floorplanWidth, floorplanHeight);
CGSize sizeOfScene = CGSizeMake(floorplanWidth, floorplanHeight);
float minimumScale = 1;//This is the minimum scale, set it to whatever you want. 1.0 = default
_sceneScrollView.maximumZoomScale = 4.0;
_sceneScrollView.minimumZoomScale = minimumScale;
_sceneScrollView.zoomScale = .1;
[_sceneScrollView setContentMode:UIViewContentModeScaleAspectFit];
[_sceneScrollView sizeToFit];
[_sceneScrollView setContentSize:sizeOfScene];
[_sceneScrollView addSubview:[director view]];
There's actually a lot of things wrong with what I see above that leads me to believe that you should probably look up more information regarding iOS and scrollviews. The first thing is that a scrollView has a contentSize that's either intrinsically determined via auto layout, or via code by setting the contentSize. In this case, your contentSize would be set via setting the image. The second thing to note is when you zoom in using a scrollView, you provide a view to zoom in on. This view and it's respective subviews will be zoomed in on. I see that your green dot is in the storyboard and is a direct subview inside the scrollview's contentView, this is incorrect. Your green dot will need to be either a subview of your own containerView with the UIImageView and your dot in it, or a subview of your UIImageView (which is sometimes wanky in the storyboard, you'll you'll want to add that in code). To note, UIImageViews have their own scale factor inside of them versus iOS's scale factor. So if you're attempting to dynamically mark anything in a UIImageView, keep that in mind.
I'm building an iPad app with views that are split horizontally and animate in from the top and bottom (think of jaws sliding closed and open to appear and disappear respectively).
My problem is the layout of the custom jaws subview is broken only when the view loads in a landscape orientation. (The jaws-view container loads at the proper size, but the subsequent subviews for the top and bottom half are too tall, and going off the screen. They are the correct width though.)
I can start in portrait and then rotate and everything is arranged correctly.
I've tried setting the frame of the new view to the bounds of the original in a bunch of places (as suggested by many answers that didn't work for me, links upon request) but either haven't found the right spot, or need something more.
Do I need to do anything special to get the size to propagate? Is there a point before which I should not do animation? (I'm trying to move the top and bottom in my new view controller's viewDidLoad.)
The solution to this required 2 parts.
The first was described in this answer:
https://stackoverflow.com/a/8574519/1143123
which describes using viewWillAppear method instead of viewDidLoad (called earlier and "incorrect" values for bounds). This solved the problem of the view being layed out properly when loading that view in landscape (and propagated to subsequent rotations).
The second part was that the view could still get messed up if I started animating it and then did a rotation in the meantime. I changed my animation class to only move the center coordinate (as opposed to sliding the frame) which would have been better in the first place, but that didn't solve it. In the end I hardcoded the following in the ViewController for the class exhibiting these issues:
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// The container view should match the size of the current view
gameView.frame = self.view.bounds;
CGFloat width = self.view.bounds.size.width;
if(roundInProgress) {
gameView.jawsTop.frame = CGRectMake(0, 0, width, self.view.bounds.size.height/2);
gameView.jawsBottom.frame = CGRectMake(0, self.view.bounds.size.height/2, width, self.view.bounds.size.height/2);
} else {
// If round not in progress, game cards should be offscreen
CGFloat height = self.view.bounds.size.height/2;
gameView.jawsTop.frame = CGRectMake(0, -height, width, height);
gameView.jawsBottom.frame = CGRectMake(0, self.view.bounds.size.height, width, height);
}
}
Without seeing your code my guess is that it has to do with the "Autoresize Subviews" property of your parent view and/or the autosizing set-up for your subviews. Try changing that property in Interface Builder to see if that fixes your issue.