A simple example is used for demonstration only strange features of the UIScrollView.
Have an instance of UIScrollView that occupies the entire screen area. An instance of UIView is placed on UIScrollView instance and entirely overlapping it.
ScrollView is configured so that all swipes on him transferred in the overlapping View and the scroll didn't happen. So it should work, all swipes redirected to the view, since it completely overlaps the ScrollView.
But there is a region located in the lower left corner of the screen, when you swipe where sometimes (not always) starts scrolling.
What is this magical area? How to avoid this behavior?
Repeated only on the device (tried on iPhone6, iPhone6+, iPad Air), on the simulator was not able to repeat.
That's how we create and custom views:
- (void)viewDidLoad
{
[super viewDidLoad];
MyScrollView *sv = [[MyScrollView alloc] initWithFrame:self.view.bounds];
sv.canCancelContentTouches = NO;
sv.delaysContentTouches = NO;
sv.multipleTouchEnabled = NO;
sv.contentSize = CGSizeMake(sv.bounds.size.width, sv.bounds.size.height + 300);
sv.backgroundColor = [UIColor redColor];
[self.view addSubview:sv];
UIView *topView = [[UIView alloc] initWithFrame:sv.bounds];
topView.backgroundColor = [UIColor greenColor];
[sv addSubview:topView];
}
MyScrollView has a single overridden method:
- (BOOL)touchesShouldBegin:(NSSet *)touches withEvent:(UIEvent *)event inContentView:(UIView *)view
{
return YES;
}
Thanks, sorry for my English.
Related
I have tried Adding UIScrollView to a UIViewController, and I can never get my view to move down when I scroll, either on a simulator on a real device. I have also tried increasing the size, so my code is as follows:
+(void)addScrollViewToView:(UIView *)view
{
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, view.bounds.size.width * 2, view.bounds.size.height * 2)];
scrollView.backgroundColor = [UIColor clearColor];
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = YES;
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.contentSize = CGSizeMake(view.bounds.size.width * 2, view.bounds.size.height * 2);
[view insertSubview:scrollView atIndex:0];
//[view sendSubviewToBack:scrollView]; - commented out
}
It is called within viewDidLoad like so:
#implementation MyLoginViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[Utils addScrollViewToView:self.view];
self.passwordTextField.secureTextEntry = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:#selector(dismissKeyboard)];
[self.view addGestureRecognizer:tap];
} // viewDidLoad method ends here
There is never any response when I try and scroll up with my mouse on a simulator. There is a background image to my view controller that I created using a storyboard. Would changing the index make any difference?
It happen because you add gesture recognizer. Gesture recognizer blocks and not pass your touch.
You can make this for dismiss keyboard with UIScrollView:
+(void)addScrollViewToView:(UIView *)view
{
// your code
scrollView.keyboardDismissMode = UIScrollViewKeyboardDismissModeOnDrag;
}
And remove UITapGestureRecognizer.
Or is it because your frame size is the same as your content size? The scroll view will only scroll if its content is bigger than its frame.
What did the trick was adding the view to the scrollview.
I.e.:
View >
ScrollView >
View that I wanted to scroll
Not:
View that I wanted to scroll >
ScrollView.
I have a mainScrollView scrolling horizontally with photos added to it and mainScrollView's contentSize is set according to the number of photos.
To achieve the effect of scrolling to the end and swiping in a UIView with display info for user, I added the overlayScrollView to mainScrollView's subviews.
overlayScrollView contains the contentView positioned outside the frame.
CGRect frame = self.mainScrollView.frame;
frame.origin.x = CGRectGetWidth(frame) * (count-1);
UIScrollView *overlayScrollView = [[UIScrollView alloc] initWithFrame:frame];
[self.mainScrollView addSubview:overlayScrollView];
// moves contentView one frame beyond
frame.origin.x = CGRectGetWidth(frame);
UIView *contentView = [[UIView alloc] initWithFrame:frame];
contentView.backgroundColor = [UIColor redColor];
[overlayScrollView addSubview:contentView];
overlayScrollView.contentSize = CGSizeMake(overlayScrollView.frame.size.width * 2, overlayScrollView.frame.size.height);
overlayScrollView.pagingEnabled = YES;
I searched through SO on nested UIScrollViews and so subclassed UIScrollView for mainScrollView with the following method:
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
for (UIView *child in self.subviews){
if([child isMemberOfClass:[UIScrollView class]])
if(CGRectContainsPoint(child.frame, point))
return child;
}
return self;
}
But it doesn't work. My code could be wrong. hitTest did return child (overlayScrollView) but did not scroll. Any suggestions how to code this?
I am creating an app with cards. In my mainViewController, I have this code:
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
where CardView is a subclass of UIView. Each Card is a unique cardView and in CardView.m I do have:
#implementation CardView
{
UIImageView *_backImageView;
UIImageView *_frontImageView;
CGFloat _angle;
}
#synthesize card = _card;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
self.backgroundColor = [UIColor clearColor];
[self loadBack];
self.userInteractionEnabled=YES;
}
return self;
}
- (void)loadBack
{
if (_backImageView == nil)
{
_backImageView = [[UIImageView alloc] initWithFrame:self.bounds];
_backImageView.image = [UIImage imageNamed:#"Back"];
_backImageView.contentMode = UIViewContentModeScaleToFill;
[self addSubview:_backImageView];
}
}
and the implementations for other functions .
Since in order to win space, one card is placed on top of the others (half of teh card is visible and the rest is covered by the next card and so on), I want to identify touches on each card.
If I place that code in CardView:
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Card is touched");
}
it is never called. If I place it in the GameView Controller it is called anywhere that I will touch, but I do not know how to identify which cardView is called. Can you give me a hint?
EDIT:
I decided to use gestures. Therefore in my mainViewController changed the code to this:
for (PlayerPosition p = startingPlayer.position; p < startingPlayer.position + 4; ++p)
{
CardView *cardView = [[CardView alloc] initWithFrame:CGRectMake(0, 0, CardWidth, CardHeight)];
cardView.card = [player.closedCards cardAtIndex:t];
cardView.userInteractionEnabled=YES;
[self.cardContainerView addSubview:cardView];
[cardView animateDealingToBottomPlayer:player withIndex:t withDelay:delay];
delay += 0.1f;
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
[cardView addGestureRecognizer:recognizer];
}
but this is never called.
-(void)cardSelected:(UITapGestureRecognizer *)recognizer
{
NSLog(#"Card Selected with gestures");
}
Why is that?
EDIT 2:
I have also tried adding this:
self.cardContainerView.userInteractionEnabled=YES;
or changing this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(cardSelected:)];
to this:
UITapGestureRecognizer *recognizer=[[UITapGestureRecognizer alloc]initWithTarget:self .cardContainerView action:#selector(cardSelected:)];
but none of them worked.
If you instead add a tap gesture recogniser to each card view you can get a callback to a method you specify and the gesture is connected to the view so you can directly get a reference to it.
Your CardView (which I guess is a subclass of UIView?) has 2 subviews, which are image views:
UIImageView *_backImageView;
UIImageView *_frontImageView;
You may want to set userInteractionEnabled to YES on one or both of them (depending on when the card should be tappable and when the subviews are shown and hidden).
You can also act as the delegate of the gesture (if you need to, or just temporarily to debug that the gesture is getting triggered but blocked by something other gesture).
I've got a UIScrollView (A) parent on the screen, inside it's content I have two controls -
another UIScrollView (B) at the Top an an UIView (C) at the bottom,
A is full screen (460px)
B 460px but content is longer then the screen (600px) so it has it's scrolling inside
C 460px fixed
also paging is enabled so B is the 1st page and C is the 2nd,
When I pan down B is scrolling and when it reaches the bottom it's bounces instead of pulling view C, if I set the bounce to NO then it's stuck at the bottom and only if I raise the finger and pan again it pulls the view C..
I saw some related questions but non of them helped me (How to steal touches from UIScrollView?)
a code sample to recreate the situation
(or download from my dropbox https://www.dropbox.com/s/f9j0vkg902214ab/Test2.zip)
- (void)viewDidLoad{
[super viewDidLoad];
// create main scroll
UIScrollView *scrollA = [[UIScrollView alloc] initWithFrame:self.view.frame];
scrollA.pagingEnabled = YES;
scrollA.bounces = YES;
[self.view addSubview:scrollA];
// create top scroll B
UIScrollView *scrollB = [[UIScrollView alloc] initWithFrame:self.view.frame];
scrollB.backgroundColor = [UIColor greenColor];
scrollB.bounces = YES;
[scrollA addSubview:scrollB];
// create something to put in B
CGRect frameViewB = scrollB.frame;
frameViewB.origin.x = 30;
frameViewB.size.width = 260;
frameViewB.size.height = 600;
UIView *viewInsideB = [[UIView alloc] initWithFrame:frameViewB];
viewInsideB.backgroundColor = [UIColor blueColor];
[scrollB addSubview:viewInsideB];
[scrollB setContentSize:viewInsideB.frame.size];
// create bottom view
CGRect frameC = self.view.frame;
frameC.origin.y = 460;
UIView *viewC = [[UIView alloc] initWithFrame:frameC];
viewC.backgroundColor = [UIColor yellowColor];
[scrollA addSubview:viewC];
// set content for 2 pages
[scrollA setContentSize:CGSizeMake(320, 920)];}
Thanks
I have a little bit specific question. It might not matter for most people but I have had to deal with it and I had to solve the issue described below. I tried to find some information about it using Google and the Apple SDK documentation but did not succeed.
I was a designing a screen where there were many images in horizontal scrolls. There three three same scrolls. Every scroll had title. I have implemented custom class derived from UIView and placed there UIScrollView for scroll and UILabel for title text:
#interface MyView : UIView {
UIScrollView *iScrollView;
UIView *iTitleView;
}
I then put objects of this class on the view of a UIViewController:
#implementation MyViewController
- (void) viewDidLoad {
[super viewDidLoad];
...
iScrollViewTop = [[MyView alloc] init];
[self.view addSubview:iScrollViewTop];
...
}
#end
When I filled the internal scroll view with images and ran my application it looked OK. But there was some strange behavior. First, scroll did not have bounces as if I had set
iScrollView.bounces = NO;
and second, when I swiped to scroll, after the scroll stopped, the scroll bar did not disappear within one second. It was strange for me, because when I usually create a UIScrollView and add it to the UIViewController's view it has bounces and scroll bar disappears immediately when it stops. I tried to change UIScrollView's properties, such as directionalLockEnabled, pagingEnabled, canCancelContentTouches, delaysContentTouches, decelerationRate and others. In fact, I have tried to change almost all properties of UIScrollView but I could not get the scroll bars to immediately disappear.
If I try to add UIScrollView instead MyView to the UIViewController.view, it bounces and scroll bar disappears immediately after it stops. Also I get correct behavior if I subclass MyView from UIScrollView but in this case I cannot manage the title label because it scrolls together with other content.
So here are my questions:
Do you know why I am seeing this behavior?
How can I get "usual" behavior for scroll encapsulated by UIView?
ok, hacky code follows, so ignore all my other issues, but follow this pattern (westie.jpg = image that was 360x200)
#interface MyView : UIView
{
UIScrollView *sv;
UILabel *l;
}
-(MyView*)initWithFrame:(CGRect)frame;
#end
#implementation MyView
-(MyView*)initWithFrame:(CGRect)frame;
{
self = [super initWithFrame:frame];
sv = [[UIScrollView alloc] initWithFrame:CGRectMake(0,0,360,200)];
sv.scrollEnabled = YES;
sv.contentSize = CGSizeMake(360*3,200);
[self addSubview:sv];
UIImage *i1 = [UIImage imageNamed:#"westie.jpg"];
UIImageView *iv1 = [[UIImageView alloc] initWithImage:i1];
iv1.frame = CGRectMake(360*0, 0, 360, 200);
[sv addSubview:iv1];
UIImageView *iv2 = [[UIImageView alloc] initWithImage:i1];
iv2.frame = CGRectMake(360*1, 0, 360, 200);
[sv addSubview:iv2];
UIImageView *iv3 = [[UIImageView alloc] initWithImage:i1];
iv3.frame = CGRectMake(360*2, 0, 360, 200);
[sv addSubview:iv3];
l = [[UILabel alloc] initWithFrame:CGRectMake(10, 10, 100, 20)];
l.text = #"Hello World";
l.backgroundColor = [UIColor blueColor];
[self addSubview:l];
return self;
}
#end
later, in your outer view creation:
[window addSubview:[[MyView alloc] initWithFrame:CGRectMake(0, 20, 360, 200)]];