I have an UIScrollView with UIViews being added by lazy-loading algorithm. The UIViews contain previews of previous and next image, so at some point the actual views have to be used more than once (e.g. preview of the 2nd picture is NEXT now, but after 2 page scrolls it is PREVIOUS).
All the UIViews are stored in NSMutableArray and are added dynamically. I have the code, which checks, whether particular UIView is already added to the UIScrollView. If not, the code checks, is there needed UIView in NSMutableArray and if not, creates a new one...
The problem occurs when I try to get an UIView from NSMutableArray, change it's position on the screen and add to UIScrollView again. It just changes it's position and I see white squares where some UIViews have to be.
Is it possible to have 2 UIViews with pointer referred to one memory location or UIImage but with only one different parameter (position)? Is it good idea to copy already created UIView to NSMutable array twice to use with different position? (Win: creation process takes place only once, loss: amount of memory required is doubled).
From my point of view, I havent fount the good solution, but this worked for me - I just implemented copy method in UIView subclass, something like this:
- (ExhibitPreview *)copy {
ExhibitPreview *exhibitPreview =[[ExhibitPreview alloc] initWithImage:self.image];
exhibitPreview.number.text = self.number.text;
exhibitPreview.author.text = self.author.text;
exhibitPreview.title.text = self.title.text;
exhibitPreview.contentMode = UIViewContentModeScaleAspectFill;
exhibitPreview.clipsToBounds = YES;
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
CGRect frame = CGRectMake(0.0f, 0.0f, screenWidth/2, PREVIEW_HEIGHT);
exhibitPreview.frame = frame;
return exhibitPreview;
}
The first view is view itself and the second I create is a copy.
Related
I'd like to implement a "zoom" effect on a paging UIScrollView that I've created, but I am having a lot of difficulty. My goal is that as a user begins to scroll to the next page, the current page zooms out to become a little bit smaller. As the next page comes into view, it zooms in until it becomes its full size. The closest thing I could find to an example was this...
https://www.pinterest.com/pin/147141112804210631/
Can anyone give me some pointers on how to accomplish this? I've been banging my head against a wall for the last 3 days on this.
I would recommend using the scrollView.contentOffset.y of your paginated UIScrollView to keep track of the scroll and to use that value to animate the transform of your views inside the UIScrollView.
So add your paginated scrollview and make self as delegate.
paginatedScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, [[self view] bounds].size.width, [[self view] bounds].size.height-paginatedScrollViewYOffset)];
[self.view addSubview:paginatedScrollView];
paginatedScrollView.pagingEnabled = YES;
[paginatedScrollView setShowsVerticalScrollIndicator:NO];
[paginatedScrollView setShowsHorizontalScrollIndicator:NO];
[paginatedScrollView setAlwaysBounceHorizontal:NO];
[paginatedScrollView setAlwaysBounceVertical:YES];
paginatedScrollView.backgroundColor = [UIColor clearColor];
paginatedScrollView.contentSize = CGSizeMake([[self view] bounds].size.width, [[self view] bounds].size.height*2); //this must be the appropriate size depending of the number of pages you want to scroll
paginatedScrollView.delegate = self;
Then use the delegate method scrollViewDidScroll to keep track of the scrollView.contentOffset.y
- (void) scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(#"Scroll Content Offset Y: %f",scrollView.contentOffset.y);
//use here scrollView.contentOffset.y as multiplier with view.transform = CGAffineTransformMakeScale(0,0) or with view.frame to animate the zoom effect
}
Use this Code scrollview its zoom in when scroll next page, the code is given below,
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
GridCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:#"CollectCell" forIndexPath:indexPath];
cell.myscrollview.minimumZoomScale = 5.0;
cell.myscrollview.zoomScale = 5.0;
cell.myscrollview.contentSize = cell.contentView.bounds.size;
return cell;
}
if you change the zoom scale value its automatically zoom in or zoom out to be showed when scroll next or previous page.
hope its helpful.
I actually just posted an answer to a very similar question, where somebody tried to achieve this effect using a UICollectionView. The link to my answer is here: https://stackoverflow.com/a/36710965/3723434
Relevant piece of code I will post here:
So another approach would be to to set a CGAffineTransformMakeScale( , ) in the UIScrollViewDidScroll where you dynamically update the pages' size based on their distance from the center of the screen.
For every page, calculate the distance of its center to the center of yourScrollView
The center of yourScrollView can be found using this nifty method: CGPoint point = [self.view convertPoint:yourScrollView.center toView:*yourScrollView];
Now set up a rule, that if the page's center is further than x away, the size of the page is for example the 'normal size', call it 1. and the closer it gets to the center, the closer it gets to twice the normal size, 2.
then you can use the following if/else idea:
if (distance > x) {
page.transform = CGAffineTransformMakeScale(1.0f, 1.0f);
} else if (distance <= x) {
float scale = MIN(distance/x) * 2.0f;
page.transform = CGAffineTransformMakeScale(scale, scale);
}
What happens is that the page's size will exactly follow your touch. Let me know if you have any more questions as I'm writing most of this out of the top of my head).
I've done some work on stylized app guide page before.
For Me, I would use CADisplayLink to track the contentOffset.x of the scrollView, associate the value with your animation process. Don't put your views on the scrollView, put them on an overlay view of this scrollView.
This solution follows the philosophy: Fake it before you make it.
Based on CADisplayLink and physics simulation of UIScrollView, you will get smooth animation. Believe me.
What you really want isn't a UIScrollView, it's a UICollectionView with a custom layout. UICollectionViewLayoutAttributes has a transform property that you can set.
Say for example, in layoutAttributesForElementsInRect::
override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
guard let attributes = super.layoutAttributesForElementsInRect(rect) else {
return nil
}
return attributes.map { attribute -> UICollectionViewLayoutAttributes in
if attribute.frame.origin.y < 0 {
let scale = -attribute.frame.origin.y / attribute.frame.height
attribute.transform = CGAffineTransformMakeScale(scale, scale)
}
return attribute
}
}
Here, you're filtering by if the element is on the screen (so non-visible elements won't be counted) and checking to see if the y offset is less than 0. If it is, you take the difference between the negated y value and the item's height and turn that into a proportional scale.
You can do it however you want, if you want the scale to be between 1 and 0.5 for example. I like this way of doing things over mucking around with a scroll view.
I am a beginner developing an iOS game using sprite kit. I would like to have 'pop-up' menu's that display objects (skspritenodes) such as daily rewards or settings/pause menu. The settings pop-up for example should be able to be accessed on the main home screen or during the game. Currently I have one view controller and the home scene and play scene are two different SKScenes. For reusability (and a clean project, as well as learning) I would like to have these pop-ups be their own class, including handling touches. Currently, I have a class for the settings pop-up that returns an SKNode, and this sknode contains several skspritenodes (the buttons/images that correspond to a settings pop up including enabling/disabling sound etc). However, I have to duplicate the touches code in both my home scene and my play scene to interact with this. (I currently have, in each skscene's touchesbegan method, "if nodeSettings !=nil", check the name of the skspritenode that corresponds to the touch-location, then call a method in the setting-pop-up class passing the name of the skspritenode clicked to handle interactions with the pop-up).
For my own knowledge and also to solve this problem, I would like to use a class that can handle the touch logic on its own (so in my play or home skscene, the only thing I do is create the pop-up. Any interaction, including dismissal, I would like to have handled in the class. No using the skscene's touches methods). I have found one 'solution' to this:
LTPopUpReward *test = [[LTPopUpReward alloc] init];
UIPopoverController *popover = [[UIPopoverController alloc] initWithContentViewController:test];
CGFloat fltHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat fltWidth = [UIScreen mainScreen].bounds.size.width;
popover.popoverContentSize = CGSizeMake(fltWidth/2, fltHeight/2); //your custom size.
CGRect rectPopUpReward = CGRectMake(500, 500, fltWidth/2, fltHeight/2);
[popover presentPopoverFromRect:rectPopUpReward inView:self.view permittedArrowDirections: UIPopoverArrowDirectionAny animated:YES];
and I like that you can dismiss the UIPopoverController by clicking outside of it, but this UIPopoverController only works on the iPad and doesn't work on the iPhone. I've seen this UIPopoverController for iphone not working? but was wondering if there is another way to solve my problem that Apple won't possibly disapprove of? For example, could I create a UIViewController, resize it and position it (i don't know how to do this) to simulate the pop-up?
Summarizing: my goal is to have a pop-up appear, but have all of its code (including touch) self contained within one class, and the objects on the pop-up are skspritenodes.
answering my own question: The solution I used to solve this problem is to use different SKViews. One SKView presents the main scene and another SKView presents the pop-up scene. You can adjust the size of the second SKView and even make its background transparent. Interacting with objects in this view will correctly call the touches methods in the class of the scene presented - That is this pop-up is dealt with when being created, and there is no other code dealing with it on the view-controller or main-scene's logic (all code, including creating objects and handling touch) are in the second skscene's code. The method below is called when I do "something" to call the 'pop-up'.
-(void)test4
{
skViewPopUp=nil;
scenePopUpReward=nil;
NSLog(#"test4 successfully fired");
CGFloat fltHeight = [UIScreen mainScreen].bounds.size.height;
CGFloat fltWidth = [UIScreen mainScreen].bounds.size.width;
CGRect rectPopUpReward = CGRectMake(0, 0, fltWidth/2, fltHeight/2);
skViewPopUp = [[SKView alloc] initWithFrame:rectPopUpReward];
[self.view addSubview:skViewPopUp];
skViewPopUp.allowsTransparency = YES;
scenePopUpReward = [[LTSceneStoreMain alloc] initWithSize:CGSizeMake(fltWidth/2, fltHeight/2)];
scenePopUpReward.backgroundColor = [UIColor clearColor];
[skViewPopUp presentScene:scenePopUpReward];
}
I am trying to implement iOS7 parallax effect. For this purpose I am using standard UIInterpolatingMotionEffect class.
Here is what I am trying to achieve:
Place photo inside UIView. Photo is larger than View's frame (on each side)
Set View's size, and CornerRadius (with masksToBounds = YES)
Move phone and watch parallax effect. Something similar as you are peaking inside hole :)
Almost every tutorial on web is simply moving whole view (by setting center.x) , but I don't know how to move content only (and clip it in same time). I have tried something, but obviously is not working:
Inside viewDidLoad I am doing next:
- (void)viewDidLoad
{
[super viewDidLoad];
_myView.layer.contents = (id) [UIImage imageNamed:#"dog"].CGImage;
_myView.layer.contentsGravity = kCAGravityCenter;
_myView.layer.masksToBounds = YES;
UIInterpolatingMotionEffect *horizontalMotionEffect = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:#"layer.position.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis];
horizontalMotionEffect.minimumRelativeValue = #(-80);
horizontalMotionEffect.maximumRelativeValue = #(80);
[_myView addMotionEffect:horizontalMotionEffect];
}
CALayer don't have addMotionEffect so I am accessing view.
Maybe my approach is not good from start, so if you have some other solution - it will help.
Thank you for any help.
I think a better approach here might just be to use 2 separate views.
The wrapping UIView you have now, sized to what you want with a corner radius and clipsToBounds set to YES.
a UIImageView with the image you want, added as a subview of the UIView.
Then, just apply the motion effect to the image view. This will keep the containing view static and achieve the keyhole effect you are looking for.
I design the scrollview in interface builder like this
It looks good here. But unfortunately when I run it on emulator or device
it becomes
The content in scrollview is expand outside scrollview itself and even though outside UIView that contains this scrollView.
In my viewDidLoad (panel is the container of scrollView )
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
CGFloat adjustPanelHeight = [PTTScreenScaleUtil getAdjustHeight:self.panel.frame.size.height];
CGRect panelRect = self.panel.frame;
panelRect.size.height = adjustPanelHeight;
self.panel.frame = panelRect;
UIImage *image = [UIImage imageNamed:#"panel-background"];
UIGraphicsBeginImageContext(self.panel.frame.size);
[image drawInRect:CGRectMake(0, 0, self.panel.frame.size.width, adjustPanelHeight)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.panel setBackgroundColor:[UIColor colorWithPatternImage:newImage]];
NSLog(#"scrollView Height : %f", self.scrollView.frame.size.height);
NSLog(#"scrollView contentSize Height : %f", self.scrollView.contentSize.height);
// CGRect scrollViewRect = self.scrollView.frame;
// CGRect scrollViewContentRect = self.scrollView.frame;
// NSLog(#"ScrollView Height Before : %f , After : %f", self.scrollView.frame.size.height, [PTTScreenScaleUtil getAdjustHeight:self.scrollView.frame.size.height]);
// scrollViewRect.size.width = 280;
// scrollViewRect.size.height = [PTTScreenScaleUtil getAdjustHeight:270];
// self.scrollView.frame = scrollViewRect;
// [self.detailsLabel sizeToFit];
UIView *view = [[self.scrollView subviews] objectAtIndex:0];
// [view sizeToFit];
// [self.scrollView setContentMode:UIViewContentModeScaleAspectFit];
// NSLog(#"ContentSize Height : %f", view.frame.size.height);
// scrollViewContentRect.size.height = view.frame.size.height;
NSLog(#"Bounds : %f", view.bounds.size.height);
self.scrollView.frame = CGRectMake(10, 10, 280, 270);
self.scrollView.contentSize = CGSizeMake(280, 500);
NSLog(#"Frame Height %f", self.scrollView.frame.size.height);
//[self.scrollView setContentSize: CGSizeMake(280, 1000)];
CGRect termBtnRect = self.termBtn.frame;
CGRect mailBtnRect = self.mailBtn.frame;
CGRect twitterBtnRect = self.twitterBtn.frame;
CGRect fbBtnRect = self.fbBtn.frame;
termBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
mailBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
twitterBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
fbBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
self.termBtn.frame = termBtnRect;
self.mailBtn.frame = mailBtnRect;
self.twitterBtn.frame = twitterBtnRect;
self.fbBtn.frame = fbBtnRect;
}
All the log return 270.0
PS. the scroll bar is correct even though the content goes outside but the scroll bar is working correctly (stay in the scrollview's frame as arrange in interface builder)
I have no idea how can I solve this.
Anyone help me please.
Thanks you.
Solve it by creating new view controller in interface builder and redo the same process with careful and bingo. It works.
When I compare both two view controller I realise that the wrong one UIScrollView Clip Subviews is unchecked. When check it the problem solve.
I just struggled with this for an hour and had a head smack moment.
In my case, I had a UIView on the scene in the Storyboard. At some point I decided I needed it to be a UIScrollView instead (as opposed to the original plan which was to embed the UIScrollView in a UIView)
I went ahead and changed the class on the UIView to UIScrollView. IB changed it to Scroll View in the Document Outline, I figure I'm good, right?
And then I see the behavior you describe.
At some point it hits me that this isn't sufficient. Apparently adding a UIScrollView via IB does some things differently than adding a UIView and just changing class isn't enough. And this is probably the reason re-doing it from scratch fixed it for you.
So for anyone who runs into this in the future, make sure you added the UIScrollView via IB instead of a UIView
I was having the same problem as described in this post. I tried multiple combinations of solutions that did not work, including:
putting the scroll view inside a view with Clip To Bounds = YES
putting a view inside the scroll view with Clip To Bounds = YES, that then contained my child view
putting a Container View inside the scroll view, and then embedding my subview
rebuilding the Interface Builder files completely
every combination of autosizing mask options systematcially for both the scroll view and container view
clip to bounds enabled or disabled for every single element systematically
The child view in question had previously worked inside a scroll view, but wouldn't in this one case where the content blew outside the bounds of the scroll view.
In the end, I implemented the solution in code as I could find no way to get Interface Builder to co-operate:
// There are two scroll areas on the screen, the left view and the right view.
// We want the right view to contain a scrollable area with another child view controller
// we designed in Interface Builder.
// create a scroll view to fill the right view with a scrollable area
CGSize rightFrameSize = self.rightView.bounds.size;
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake (0, 0, rightFrameSize.width, rightFrameSize.height)];
scrollView.contentSize = CGSizeMake(640, 1352);
[self.rightView addSubview:scrollView];
// now create our child view controller from Interface Builder and add it to the scroll view
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"CustomerAddress" bundle:[NSBundle mainBundle]];
detailsView = [sb instantiateViewControllerWithIdentifier:#"CustomerDetailsView"];
detailsView.delegate = self;
detailsView.customer = _customer;
[scrollView addSubview:detailsView.view];
You could of course get the scrollView.contentSize from the child view controller you constructed in Interface Builder using scrollView.contentSize = detailsView.view.frame.size.
I have a love/hate relationship with Interface Builder... most days I love it, but some days we argue and I wish we'd never met... :)
only make cliptobound=YES in storyboard if you changed UIView to UIScrollView
Studying iOs Development by - The Big Nerd Ranch Guide" (Conway and Hillegass)
Chapter "Subclassing UIView and UIScrollView"; Panning and paging.
The following chunk of code being typed in the
- (BOOL)application: didFinishLaunchingWithOptions: method.
(HypnosisView - is a custom made class that actually performs the drawing on the screen.)
Can't understand following code:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
//-------Adding a scrool option-----------
CGRect screenRect=[[self window] bounds];
// create the UIScrollView to have the size of the window, matching its size
UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:screenRect];
[scrollView setPagingEnabled:YES];
[[self window] addSubview:scrollView];
// create the HypnosisView with a frame that is twice the size of the screen (with a big
// width)
CGRect bigRect = screenRect;
bigRect.size.width *=2.0;
HypnosisView *view=[[HypnosisView alloc] initWithFrame:screenRect];
// add the HypnosisView as a subview of the scrollView istead of the window
[scrollView addSubview:view];
// move the ractangle for the other HypnosisView to the right, just off the screen
screenRect.origin.x = screenRect.size.width;
HypnosisView *anotherView = [[HypnosisView alloc] initWithFrame:screenRect];
[scrollView addSubview:anotherView];
// tell the scrollView how big its virtual world is
[scrollView setContentSize:bigRect.size];
So our goal is to create a view instance with an width bigger than the iphone screen.
First we are declaring new variable "screenRect" that has bounds of a "window".
Then we are creating an instance of "UIScrollView" that has frame dimensions same as the
"screenRect" same as the window.
Making paging enabled.
Adding our newly created "scrollView" to the hierarchy of views.
So we have parent "window" and child "scrollView" (that has same dimensions as our main window)
Declaring a new variable "bigRect", and making it equal to our previously declared "screenRect".
Setting bigRect's "width" property to be twice as much.
Creating a new "view" object that is an instance of our custom made Hypnosis class that actually performs the drawing. We set our view's frame to be the same as our "screenRect" frame.
Adding our newly created "view" to the hierarchy of views. Now we have 3 level hierarchy: UIWindow--> UIScrollView-->HypnosysView
9.Now here, I don't understand what this line of code does and why do we need it (screenRect.origin.x = screenRect.size.width;)
10). Why are we creating another instance of HypnosisView in the next line?
11). at the end we notify scrollView about how big its size.
9.Now here, I don't understand what this line of code does and why do we need it (screenRect.origin.x = screenRect.size.width;)
10). Why are we creating another instance of HypnosisView in the next line?
The example will display 2 HypnosisViews which are side by side in a scroll view. The second one is off screen. So you have to drag/page the scroll view to see it.
screenRect.origin.x = screenRect.size.width
This just positions the 2nd hypnosis view to the right of the fist one.