Has anyone managed to successfully add a header or footer view to a WKWebView ScrollView?
I'm currently trying to do this using the method described here for a UIWebView Adding a header view to a UIWebView similar to Safari and Articles.
When this method is used in a WKWebView the content view origin.y is correctly changed but content is cut off at the bottom.
Using the scroll view content offset is also not possible as it breaks fixed positioned CSS elements in the web view.
In webView Delegate method
- (void)webViewDidFinishLoad:(UIWebView *)webView
add the following codebase,
mainWebViewObj.scrollView.contentInset = UIEdgeInsetsMake(headerView.frame.size.height,0.0,headerView.frame.size.height,0.0);
mainWebViewObj.scrollView.backgroundColor = [UIColor whiteColor];
if(![headerView superview])
{
[webView.scrollView addSubview:headerView];
[webView.scrollView bringSubviewToFront:headerView];
}
[mainWebViewObj.scrollView setContentOffset:
CGPointMake(0, -mainWebViewObj.scrollView.contentInset.top) animated:NO];
this worked perfect for me. Hope it solves your problem.
Here's an example that I think does as you describe. It offsets the web content by setting contentInset on the scrollView, and by offsetting the header view frame by a negative amount:
#implementation ViewController
{
WKWebView* _webView;
UIView* _headerView;
}
- (void)viewDidLoad
{
[super viewDidLoad];
_webView = [[WKWebView alloc] initWithFrame: self.view.bounds];
[self.view addSubview: _webView];
[_webView loadRequest: [NSURLRequest requestWithURL: [NSURL URLWithString: #"http://www.stackoverflow.com"]]];
[_webView.scrollView setContentInset: UIEdgeInsetsMake(100, 0, 0, 0)];
_headerView = [[UIView alloc] initWithFrame: CGRectMake(0, -100, 375, 100)];
_headerView.backgroundColor = [UIColor redColor];
[_webView.scrollView addSubview: _headerView];
}
- (void) viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
_webView.frame = self.view.bounds;
CGRect f = _headerView.frame;
f.size.width = _webView.bounds.size.width;
_headerView.frame = f;
}
Related
I am trying to set the webview's scrollview using the code below but it disabled the scrolling/interaction of the webview.
[webView sizeToFit];
webView.scrollView.scrollEnabled = YES;
webView.scrollView.contentSize = CGSizeMake(375, rfqWeb.scrollView.contentSize.height + 700);
rfqWeb.userInteractionEnabled = YES;
If you want to change the "scrollable size", I suggest to do:
- (void)setInsetsTop:(CGFloat)top bottom:(CGFloat)bottom {
self.webview.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.view.frame.size.height - bottom);
[[self.webview scrollView] setContentInset:UIEdgeInsetsMake(top, 0, bottom, 0)];
}
I call it from another class
[htmlWebViewController setInsetsTop:0.0f bottom:44.0f];
If you use it in the same class just use
[self setInsetsTop:0.0f bottom:44.0f];
I'm trying to figure out an approach to build something like the image below, which is a list of items that when a section is clicked slides out content. It's a really common UX on most websites and what not. My idea is to have each gray box (button) slide out a UIView containing some other items. I'm still new to iOS development but I'm struggling to find how you can animate a UIView to slide down and push the content below it down as well. Hoping some one can give me a good starting point or point to some info outside the realm of the apple docs.
Thanks!
So if you just have a few views, I would not recommend the UITableView approach, since it is not so easy to customize with animations and table views usually want to fill the whole screen with cells. Instead write a expandable UIView subclass that has the desired two states. Add a method to switch between extended and collapsed state. On expanding/collapsing adjust their positions so that they always have enough space.
I provide you an example of views adjusting their frames. I guess it should be easy to do the same with auto layout constraints: give the views a fixed height constraint and change this on collapsing/expanding. The same way set the constraints between the views to be 0 so that they are stacked on top of each other.
Expandable View:
#interface ExpandingView(){
UIView *_expandedView;
UIView *_seperatorView;
BOOL _expanded;
}
#end
#implementation ExpandingView
- (id)init
{
self = [super initWithFrame:CGRectMake(15, 0, 290, 50)];
if (self) {
_expanded = NO;
self.clipsToBounds = YES;
_headerView = [[UIView alloc] initWithFrame:self.bounds];
_headerView.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
[self addSubview:_headerView];
_seperatorView = [[UIView alloc] initWithFrame:CGRectMake(0, self.bounds.size.height-1, self.bounds.size.width, 1)];
_seperatorView.backgroundColor = [UIColor lightGrayColor];
[self addSubview:_seperatorView];
_expandedView = [[UIView alloc] initWithFrame:CGRectOffset(self.bounds, 0, self.bounds.size.height)];
_expandedView.backgroundColor = [UIColor blueColor];
[self addSubview:_expandedView];
}
return self;
}
- (void)layoutSubviews{
[self adjustLayout];
}
- (void)adjustLayout{
_headerView.frame = CGRectMake(0, 0, self.bounds.size.width, 50);
_seperatorView.frame = CGRectMake(0, 49, self.bounds.size.width, 1);
_expandedView.frame = CGRectMake(0, 50, self.bounds.size.width, self.bounds.size.height-50);
}
- (void)toggleExpandedState{
_expanded = !_expanded;
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, _expanded?200:50);
[self adjustLayout];
}
#end
ViewController:
#interface ExpandingViewController (){
NSArray *_expandingViews;
}
#end
#implementation ExpandingViewController
- (void)viewDidLoad
{
[super viewDidLoad];
_expandingViews = #[
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
[[ExpandingView alloc] init],
];
for(ExpandingView *view in _expandingViews){
[view.headerView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(expandingViewTapped:)]];
[self.view addSubview:view];
}
}
- (void)viewWillLayoutSubviews{
int y = 100;
for(ExpandingView *view in _expandingViews){
view.frame = CGRectOffset(view.bounds, (CGRectGetWidth(self.view.bounds)-CGRectGetWidth(view.bounds))/2, y);
y+=view.frame.size.height;
}
}
- (void)expandingViewTapped:(UITapGestureRecognizer*)tapper{
ExpandingView *view = (ExpandingView*)tapper.view.superview;
[UIView animateWithDuration:0.5 delay:0 usingSpringWithDamping:0.8 initialSpringVelocity:0 options:0 animations:^{
[view toggleExpandedState];
[self.view layoutIfNeeded];
} completion:nil];
}
I’m adding a UISegmentedControl right under the NavigationBar in a UITableViewController. This is the code.
- (void)viewDidLoad
{
[super viewDidLoad];
self.navigationBar = self.navigationController.navigationBar;
UIView *segmentView=[[UIView alloc] initWithFrame:CGRectMake(0, self.navigationBar.frame.size.height, self.navigationBar.frame.size.width, 50)];
[segmentView setBackgroundColor:[UIColor whiteColor]];
segmentView.alpha = 0.95;
self.tabSegmentedControl = [[UISegmentedControl alloc] initWithItems:[NSArray arrayWithObjects:#"Favourites", #"All", nil]];
self.tabSegmentedControl.frame = CGRectMake(20, 10, self.navigationBar.frame.size.width - 40, 30);
[self.tabSegmentedControl addTarget:self action:#selector(tabChanged:) forControlEvents:UIControlEventValueChanged];
[segmentView addSubview:self.tabSegmentedControl];
[self.navigationBar addSubview:segmentView];
[self.tabSegmentedControl setSelectedSegmentIndex:1];
}
The view and the SegmentedControl appear on the screen well, but they are not clickable. The selector doesn’t get executed when tapped on the SegmentControl; it doesn’t even switch tabs! In fact, the stuff that is underneath the segmentView (items in the TableView) get clicked when you tap on it. I have tried but failed to understand why this is happening! Any suggestions would be helpful!
You are adding a view below the bounds of its super view. You may see the view however you cannot click it because it is out of bounds. If you set the property of the navigation bar clipsToBounds to YES you should see that the view disappears. What you need to do is add the segment controller to the table view. Here is an example:
- (void)viewDidLoad
{
[super viewDidLoad];
...
[self.view addSubview: self.segmentView]; // need to keep a pointer to segmentView
self.tableView.contentInset = UIEdgeInset(self.segmentView.frame.size.height, 0,0,0);
}
-(void) scrollViewDidScroll:(UIScrollView*) scrollView{
CGRect rect = self.segmentView.frame;
rect.origin = self.tableView.contentOffset;
self.segmentView.frame = rect;
}
I have a UIScrollView with a UIView inside it, in side the UIView I made a button to add textLabels to it.
and ideally I would want a really big canvas and be able to put text on it and pan and zoom around. however with the UIScrollView it does zoom, but does not pan at all
It seems that when I remove the UIView that i add inside the UIScrollView it works fine.
heres viewDidLoad:
[super viewDidLoad];
CGFloat mainViewWidth = 700;
CGFloat mainViewHeight = 500;
//scrollView.contentSize = CGSizeMake(scrollView.frame.size.width * kNumberOfPages, scrollView.frame.size.height * kNumberOfPages);
//self.mainScrollView.bounds = CGRectMake(0., 0., 3000, 3000);
self.mainScrollView.scrollsToTop = NO;
self.mainScrollView.delegate = self;
self.mainScrollView.maximumZoomScale = 50.;
self.mainScrollView.minimumZoomScale = .1;
self.mainScrollView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.mainView = [[UIView alloc] initWithFrame: CGRectMake(0, 0, mainViewWidth, mainViewHeight)];
[self.mainView setUserInteractionEnabled:NO];
self.mainView.backgroundColor = [[UIColor alloc] initWithRed:0.82110049709463495
green:1
blue:0.95704295882687884
alpha:1];
[self.mainScrollView setContentSize:CGSizeMake(5000, 5000)];
[self.mainScrollView insertSubview:self.mainView atIndex:0];
Edit:
Heres the all I have for UIScrollViewDelegate
#pragma mark - Scroll View Delegate
- (void)scrollViewDidScroll:(UIScrollView *)sender {
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
//
return self.mainView;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)zoomedScrollView withView:(UIView *)view atScale:(float)scale
{
}
I just went through this exact same dilemma.
My UIScrollView exists within the storyboard, and if I add a UIView (containerView) within that storyboard to the UIScrollView, the image fails to pan. And all sorts of other centering weirdness occurs too.
But if I do it through code:
// Set up the image we want to scroll & zoom
UIImage *image = [UIImage imageNamed:#"plan-150ppi.jpg"];
self.imageView = [[UIImageView alloc] initWithImage:image];
self.containerView = [[UIView alloc] initWithFrame:self.imageView.frame];
[self.containerView addSubview:self.imageView];
[self.scrollView addSubview:self.containerView];
// Tell the scroll view the size of the contents
self.scrollView.contentSize = self.containerView.frame.size;
... then it works just fine.
In my app, I have a split screen in which the detail view is a scrollview. I have 5 tables which are subviews of my scrollview in which 3 table views are side by side on top and 2 table views are side by side on bottom
I have already implemented a way in which when I click any of the rows of any of the table in the scrollview, that view disappears and another view zooms into its position.
I write the following code in the didSelectRowAtIndexPath in the middle table subview,
CGFloat xpos = self.view.frame.origin.x;
CGFloat ypos = self.view.frame.origin.y;
self.view.frame = CGRectMake(xpos+100,ypos+150,5,5);
[UIView beginAnimations:#"Zoom" context:NULL];
[UIView setAnimationDuration:2];
self.view.frame = CGRectMake(xpos,ypos,220,310);
[UIView commitAnimations];
[self.view addSubview:popContents.view];
popContents is the view I need to zoom into to the view previously occupied by that particular table view and that happens correctly.
However the problem that I am facing is that since there is another table subview in the side, if I increase the frame size to say 250 or so, the part of the zoomed in view gets hidden by the tableview on the side ( as its as if a part of the zoomed in view goes under the tableview on the side).
Is there anyway to correct this so that my zoomed in view would not get hidden by the tableviews on its sides?
I hope I have explained my problem correctly...
UPDATE:
Here is the code I am using for adding the subviews for the scrollview
// Scroll view
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 30, 1000, 740)];
scrollView.contentSize = CGSizeMake(1000, 700);
scrollView.delegate = self;
scrollView.scrollEnabled = YES;
scrollView.showsHorizontalScrollIndicator = YES;
scrollView.backgroundColor = [UIColor scrollViewTexturedBackgroundColor];
[self.view addSubview:scrollView];
aView = [[aViewController alloc] initWithNibName:#"aViewController" bundle:nil];
aView.view.frame = CGRectMake(10, 25, 220, 310);
[aView loadList:objPatients];
[scrollView addSubview:aView.view];
bView = [[bViewController alloc] initWithNibName:#"bViewController" bundle:nil];
bView.view.frame = CGRectMake(10, 350, 220, 310);
[bView loadList:objPatients];
[scrollView addSubview:bView.view];
cView = [[cViewController alloc] initWithNibName:#"cViewController" bundle:nil];
cView.view.frame = CGRectMake(240, 25, 220, 310);
[cView loadList:objPatients];
[scrollView addSubview:cView.view];
dView = [[dViewController alloc] initWithNibName:#"dViewController" bundle:nil];
enView.view.frame = CGRectMake(240, 350, 220, 310);
[enView loadList:objPatients];
[scrollView addSubview:dView.view];
eView = [[eViewController alloc] initWithNibName:#"eViewController" bundle:nil];
eView.view.frame = CGRectMake(470, 25, 220, 310);
[eView loadList:objPatients];
[scrollView addSubview:eView.view];
say for example, I add the code for didSelectRowAtIndexPath in cViewController subview...
This is a guess since I would need to know how your table views are added to the scroll view, but the middle table view was probably added before the one on the side. Views are "stacked" in the order they're added with the last one on top. You'll need to get the scroll view to move the middle view to the front with this method
- (void)bringSubviewToFront:(UIView *)view
The best way to do that would be to create a protocol for the table views and make the scroll view the delegate. The method would be something like this
- (void) moveAViewToFront: (MyTableView *) aTableView
{
[self.view bringSubviewToFront: aTableView.view];
}
You would then call the delegate method before setting up the animation.
Edited
After a little more thought I realized that the subviews have a reference to their superview so this bit of code should provide an idea on how to solve the problem. I created a test app which has a view controller which adds two sub views. The view controller header file is MoveSubviewViewController.h
#import <UIKit/UIKit.h>
#interface MoveSubviewViewController : UIViewController
{
}
#end
and it's implementation is
#import "MoveSubviewViewController.h"
#import "MoveableSubview.h"
#implementation MoveSubviewViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Create two overlapping subviews. The blue subview will start at the top of
// the frame and extend down two thirds of the frame.
CGRect superviewFrame = self.view.frame;
CGRect view1Frame = CGRectMake( superviewFrame.origin.x, superviewFrame.origin.y,
superviewFrame.size.width, superviewFrame.size.height * 2 / 3);
MoveableSubview *view1 = [[MoveableSubview alloc] initWithFrame: view1Frame];
view1.backgroundColor = [UIColor blueColor];
[self.view addSubview: view1];
[view1 release];
// The green subview will start one third of the way down the frame and
// extend all the to the bottom.
CGRect view2Frame = CGRectMake( superviewFrame.origin.x,
superviewFrame.origin.y + superviewFrame.size.height / 3,
superviewFrame.size.width, superviewFrame.size.height * 2 / 3);
MoveableSubview *view2 = [[MoveableSubview alloc] initWithFrame: view2Frame];
view2.backgroundColor = [UIColor greenColor];
[self.view addSubview: view2];
[view2 release];
}
#end
The subview class is MoveableSubview with another simple header
#import <UIKit/UIKit.h>
#interface MoveableSubview : UIView
{
}
#end
and implementation
#import "MoveableSubview.h"
#implementation MoveableSubview
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Move this view to the front in the superview.
[self.superview bringSubviewToFront: self];
}
#end
The thing to do is to add the
[self.superview bringSubviewToFront: self];
line before setting up the animation.