Can I put a UIScrollView inside another UIScrollView - ios

I have a UIScrollView which scrolls only in vertical direction, I need to place UIScrollView which can move horizontally, like the AppStore application in apple devices. I don't want to us UICollectionView since I have static data and I have to only 3 horizontal UIScrollView

Yes you can. As UIScrollView subclasses UIView it will behave as expected when adding subviews. Each scroll view will enable scrolling based on its contentSize property.
Objective-C:
UIScrollView *horizontalScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(40.0, 40.0, 300.0, 300.0)];
horizontalScrollView.backgroundColor = [UIColor lightGrayColor];
horizontalScrollView.contentSize = CGSizeMake(2000.0, 300.0);
[self.view addSubview:horizontalScrollView];
UIScrollView *verticalScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(40.0, 40.0, 220.0, 220.0)];
verticalScrollView.backgroundColor = [UIColor greenColor];
verticalScrollView.contentSize = CGSizeMake(220.0, 2000.0);
[horizontalScrollView addSubview:verticalScrollView];
Swift:
let horizontalScrollView = UIScrollView(frame: CGRectMake(40.0, 40.0, 300.0, 300.0))
horizontalScrollView.backgroundColor = UIColor.lightGrayColor()
horizontalScrollView.contentSize = CGSizeMake(2000.0, 300.0)
self.view.addSubview(horizontalScrollView)
let verticalScrollView = UIScrollView(frame: CGRectMake(40.0, 40.0, 220.0, 220.0))
verticalScrollView.backgroundColor = UIColor.greenColor()
verticalScrollView.contentSize = CGSizeMake(220.0, 2000.0)
horizontalScrollView.addSubview(verticalScrollView)

Yes you can. But you need to differentiate the scroll views in scroll view delegate methods. Either you can use tags or if you are declaring them as global variables in the entire class,you can directly access them with their names.Eg:
UIScrollView *parentScrollView = [[UIScrollView alloc] init];
parentScrollView.delegate = self;
[self.view addSubview:parentScrollView];
UIScrollView *childScrollView = [[UIScrollView alloc] init];
childScrollView.delegate = self;
[parentScrollView addSubview:childScrollView];
Now inside delegate method you can check for the scroll view like
if(scrollview == parentScrollview)
{
// do your actions
}
This is possible only if your scroll view objects are global to the class. You can also give a tag and check for the tag in scroll view delegate method like
parentScrollView. tag = 101;
And in scroll view delegate method
if(scrollview.tag = 101)
{
// do your actions
}

yes it is possible but you have to maintain tag of scrollview for handling delegate methods of scroll view.

UIScrollView has a property called scrollEnabled, which you can set to NO to disable scrolling in your parent scroll view.
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
{
if(scrollView == innerView)
outerView.scrollEnabled = NO;
else
outerView.scrollEnabled = YES;
}
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
if(scrollView == innerView)
{
if(innerView.contentOffset.x + innerView.frame.size.width == innerView.contentSize.width)
{
outerView.scrollEnabled = NO;
}
else
{
outerView.scrollEnabled = YES;
}
}
}
Or else you can go through the below link.

Related

UIScrollView view port coordinates

This is how I am adding UIScrollView in my viewDidLoad
UIScrollView* scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, self.view.bounds.size.height)];
scrollView.backgroundColor = [UIColor redColor];
scrollView.scrollEnabled = YES;
scrollView.pagingEnabled = YES;
scrollView.showsVerticalScrollIndicator = YES;
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.contentSize = CGSizeMake(self.view.bounds.size.width , self.view.bounds.size.height * 2);
[self.view addSubview:scrollView];
scrollView.delegate = self;
When the scrollView is loaded for the first time it's coordinates are : {(0,0), (widthOfiPhone, 0), (0, heightOfiPhone), (widthOfiPhone, heightOfiPhone)}
Whenever the scroll finishes, I want to get the co-ordinates of viewport in scrollview. How to do this? Thanks
Edit:
Image Credits: https://oleb.net/blog/2014/04/understanding-uiscrollview/
First, observe scrollview agent, and then implement its proxy method
(void) scrollViewDidEndDecelerating: (UIScrollView *) scrollView {
NSLog (# "% f,% f,% f,% f", scrollView.frame.origin.x,
scrollView.frame.origin.y,
scrollView.frame.size.width,
scrollView.frame.size.height);
}
This should be possible
You can implement UIScrollView delegate method to detect when scroll is end : scrollViewDidEndDecelerating and in this method you can get coordinates of your scrollview and it's content. Btw content's cooridinate will be same with respect scrollview!!!
You can get visible rect from scrollview like,
CGRect visibleRect = [subViewOfScrollView convertRect:subViewOfScrollView.bounds toView:self.view];

Adding scrollview to a view

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.

Views disappearing from UITableViewCell when selected

I have a UITableView with custom UITableViewCells. The content of the cell is kept in a separate class extending UIView (i have 3 kind of contents which i switch by hiding and showing views, i took this approach because of the way i wanted to make the transition between cells).
So let's assume i have one of the views visible and added to the contentView of the cell. This view contains some text and some rectangles made of UIViews. Everything good till now, but the weird thing is when i touch the cell, the rectangles simply disappears, the text stays the same. Do you know what might be the problem? I tried to add the view to the backgroundView as well, it's doing the same.
- (id) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.active = NO;
state = -1;
// Touch is not working if the view has no bounds
self.backgroundView = [[UIView alloc] init];
self.backgroundColor = [UIColor clearColor];
self.selectedBackgroundView = [[UIView alloc] init];
self.selectedBackgroundView.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
self.contentView.clipsToBounds = YES;
// Add empty view
emptyView = [[View1 alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) andRadius:CELL_DOT_RADIUS];
emptyView.userInteractionEnabled = NO;
[self.backgroundView addSubview:emptyView];
The view:
- (id) initWithFrame:(CGRect)frame andRadius:(int)r {
self = [super initWithFrame:frame]; radius = r;
if (self) {
int outerlineWeight = 1;
timelineView = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width/4, frame.size.height/2, 1, 1)];
[self addSubview:timelineView];
dotView = [[UIView alloc] initWithFrame:CGRectMake(frame.size.width/4-radius, frame.size.height/2-radius, radius*2, radius*2)];
dotView.layer.cornerRadius = radius;
dotView.autoresizingMask = UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
[self addSubview:dotView];
UIView *n = [[UIView alloc] initWithFrame:CGRectMake(160, 0, 50, 20)];
n.backgroundColor = [UIColor redColor];
[self addSubview:n];
middleText = [[UILabel alloc] initWithFrame:dotView.frame];
middleText.font = [UIFont systemFontOfSize:12];
middleText.textColor = [UIColor grayColor];
middleText.backgroundColor = [UIColor clearColor];
middleText.textAlignment = NSTextAlignmentCenter;
[self addSubview:middleText];
This are the sources so you can try for yourself if you want. As you can see the orange rectangle disappears but the text not. For what i need to do nothing should disappear, in the best case i want to change their colours. http://ge.tt/6wRBObD1/v/0?c
You are adding it to background view. When selected the selected background view will display not background view. In your custom cell try
- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
[super setSelected:selected animated:animated];
if (selected) {
[self.backgroundView addSubview:emptyView];
}
else{
[self.selectedBackgroundView addSubview:emptyView];
}
// Configure the view for the selected state
}
Or add empty view to contentview
one thing for frame settings u need to override "layoutsubviews" method ...
in custom cell
init all the views in initialisation method i,e ur - (id) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier method and add it to cell
in - (void)layoutSubviews just set the frame for ur views in the cell,
use tag property to access the views in the layoutsubviews, if it is not the property
- (id) initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.active = NO;
state = -1;
// Touch is not working if the view has no bounds
self.backgroundView = [[UIView alloc] init];
self.backgroundColor = [UIColor clearColor];
self.selectedBackgroundView = [[UIView alloc] init];
self.selectedBackgroundView.backgroundColor = [UIColor clearColor];
self.clipsToBounds = YES;
self.contentView.clipsToBounds = YES;
// Add empty view
//emptyView = [[View1 alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) andRadius:CELL_DOT_RADIUS];//dont set the frame heare do it layoutsubviews
emptyView = [[View1 alloc] init];
emptyView.userInteractionEnabled = NO;
emptyView.tag = 100;
[self.backgroundView addSubview:emptyView];
- (void)layoutSubviews
{
[super layoutSubviews];
UIView *emptyView = [self viewWithTag:100];//your background view heare u can get the frame values for ur cell
emptyView.frame = self.bounds;//better do it for other views in cell also
//test it by setting some background color
}
Hope this helps u ...:)
Ok, so like i've said in my last comment the backgroundColor of any subview is the problem, in the selected state is cleared. For cells created in interface builder the problem does not occurs, my cells are created programatically.
I fixed this by drawing with CoreGraphics in drawRect method.
As with a UICollectionView a UITableView has a background view that is build before AutoLayout. If you use autoLayout you should make sure the backgroundView has a frame. Otherwise is will disappear.
In your cell subclass you can do:
-(void)setBackgroundView:(UIView *)backgroundView
{
super.backgroundView = backgroundView;
self.backgroundView.translatesAutoresizingMaskIntoConstraints = NO;
[self.backgroundView autoPinEdgesToSuperviewEdgesWithInsets:UIEdgeInsetsZero];
}
In swift you need to declare viewcontroller object globally that would result in Strong, in case if you declare locally it results in keep disappearing the cells.
var refineViewController : RefineViewController?
then you can access that controller using below code that would not result in disappearing cells.
func showRefineView(isFindHomeTab isFindHomeTab : Bool){
refineViewController = RefineViewController(nibName: String(BaseGroupedTableVC),bundle : nil)
refineViewController!.refineSearchDelegate = self
refineViewController!.view.frame = CGRectMake(0, -490, self.view.frame.size.width, self.view.frame.size.height)
UIView.animateWithDuration(0.3, delay: 0.0, options: .CurveEaseOut, animations:
{
self.refineViewController!.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)
self.refineViewController!.isFindHomeTab = isFindHomeTab
}, completion: nil)
self.view.addSubview(refineViewController!.view)
}

UIScrollView with multiple subViews

I have a UIScrollView with many other subviews inside it. Most of the subviews are UITextView's and when they are all loaded, the scrolling and everything is fine. But for one of the views, I am loading a UIView with a MKMapView and a UITextView inside of it. When the user wants to scroll the UIScrollView, they cannot touch the UIView or its contents. I cannot set setUserInteractionEnabled to NO because I need the user to be able to click on the MKMapView and then go to another UIViewController for the map. Are there any suggestions regarding this? I have my code for the above below:
CGRect dealDescRect = CGRectMake(10, 10, delegate.scrollView.frame.size.width - 22 - 20, 120);
mapView = [[MKMapView alloc] initWithFrame:dealDescRect];
mapView.layer.cornerRadius = cornerRadius;
mapView.scrollEnabled = NO;
mapView.zoomEnabled = NO;
BOOL result = [self loadAddressIntoMap];
if (result == TRUE) {
UITapGestureRecognizer* recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(handleTap:)];
[mapView addGestureRecognizer:recognizer];
}
UITextView *addressTextView = [self generateTextView:addressText :5];
addressTextView.editable = NO;
[addressTextView setFont:[UIFont systemFontOfSize:fontSize]];
[addressTextView setUserInteractionEnabled:NO];
CGRect addressTextViewFrame = addressTextView.frame;
addressTextViewFrame.origin.x = 0;
addressTextViewFrame.origin.y = 130;
addressTextViewFrame.size.height = addressTextView.contentSize.height + 15;
addressTextView.frame = addressTextViewFrame;
CGRect viewRect = CGRectMake(10, 145, delegate.scrollView.frame.size.width - 22, addressTextView.contentSize.height + 135);
viewRect.origin.x = 11;
viewRect.origin.y = startTop;
UIView *view = [[UIView alloc] initWithFrame:viewRect];
view.layer.cornerRadius = cornerRadius;
[view setBackgroundColor:[UIColor whiteColor]];
[view addSubview:mapView];
[view addSubview:addressTextView];
EDIT
For some weird reason, if I change the UIView to a UITextView, it works! Not sure what the real solution here is though. I just disable editing.
If it were me, instead of using gesture recognizers to watch for a tap on the map I'd create a UIButton of a custom type (UIButtonTypeCustom) and give it no background and no text, and place it on top of the map with the same frame as the map.
This has the benefit of preventing the user from interacting with the map, moving to the next page as you want and if the user starts scrolling even when over the map they are able to.

How-To forward pan gesture from a scrollview to another scrollview

I have two UIScrollViews and i want to let the other scrollview scroll too if the user scrolls in a scrollview. I have read a solution that involves passing the pangesturerecognizer from the originally panned scrollview to the other scrollview but if i do that the original scrollview does not scroll at all.
I found out, that there is a delegate method
(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
but if I try to hook up the delegate of the pangesturerecognizers of the scrollviews my app crashes.
Hope someone can help me.
You Just need to update the Bounds of the second ScrollView
Something like :-
CGRect updateTheViewWithBounds = viewToUpdate.bounds;
updateTheViewWithBounds.origin = scrolledView.contentOffset;
viewToUpdate.bounds = updateTheViewWithBounds;
This Will do the Job.
As Seen from comments , I will give a small Example.
Create the two scrollView onto The UiViewController
scrollOne = [[UIScrollView alloc] init];
[scrollOne setBackgroundColor:[UIColor greenColor]];
[scrollOne setFrame:CGRectMake(10, 20, 200, 300)];
[scrollOne setContentSize:CGSizeMake(600, 600)];
scrollOne.delegate = self;
scrollTwo = [[UIScrollView alloc] init];
[scrollTwo setBackgroundColor:[UIColor redColor]];
[scrollTwo setFrame:CGRectMake(230, 20, 200, 300)];
[scrollTwo setContentSize:CGSizeMake(600, 600)];
scrollTwo.delegate = self;
[self.view addSubview:scrollOne];
[self.view addSubview:scrollTwo];
Conform to the UIScrollView Delegates and implement the same.
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
if([scrollView isEqual:scrollOne])
{
CGRect updateTheViewWithBounds = scrollOne.bounds;
updateTheViewWithBounds.origin = scrollOne.contentOffset;
scrollTwo.bounds = updateTheViewWithBounds;
[scrollTwo flashScrollIndicators];
}
else if([scrollView isEqual:scrollTwo])
{
CGRect updateTheViewWithBounds = scrollTwo.bounds;
updateTheViewWithBounds.origin = scrollTwo.contentOffset;
scrollOne.bounds = updateTheViewWithBounds;
[scrollOne flashScrollIndicators];
}
}
Scrolling any of the above scrollView will scroll both the scrollView.

Resources