UIWebView Link Clicks Inside Scroll View - ios

I am facing an interesting issue. I have HTML data that I obtain from a Web service that I display inside the UIWebView and I want to open up hyperlinks in Safari when a user taps on the link.
The code I have works great for any links that are visible immediately within the Web View. I have a scroll on the View and links that are below the visible area (i.e. links that I have to scroll to) do not work when I tap them at all.
Please note that I'm disabling all scrollbars in the WebView using logic like so:
DescriptionWebView = new UIWebView(new RectangleF(0, separator.Frame.Bottom + 1, 320, 140));
DescriptionWebView.DataDetectorTypes = UIDataDetectorType.All;
DescriptionWebView.UserInteractionEnabled = true;
DescriptionWebView.Delegate = new PlanDescriptionWebViewDelegate(this);
foreach(UIView subview in DescriptionWebView.Subviews)
{
if(subview is UIScrollView)
(subview as UIScrollView).ScrollEnabled = false;
}
And I'm resizing the WebView based on the content size using logic inside of a delegate like so:
public override bool ShouldStartLoad(UIWebView webView, MonoTouch.Foundation.NSUrlRequest request, UIWebViewNavigationType navigationType)
{
NSUrl url = request.Url;
if(navigationType == UIWebViewNavigationType.Other)
{
if(url.Scheme.ToString().Equals("ready"))
{
float contentHeight = Convert.ToInt32(url.Host);
if(contentHeight < _infoView.DescriptionWebView.Frame.Height)
contentHeight = _infoView.DescriptionWebView.Frame.Height;
RectangleF scrollViewFrame = _infoView.ScrollView.Frame;
RectangleF descriptionViewFrame = _infoView.DescriptionWebView.Frame;
BeginInvokeOnMainThread(() =>
{
_infoView.DescriptionWebView.Frame = new RectangleF(descriptionViewFrame.X, descriptionViewFrame.Y, descriptionViewFrame.Width, contentHeight + 10);
//_infoView.ScrollView.ContentSize = new SizeF(scrollViewFrame.Size.Width, contentHeight + scrollViewFrame.Y + scrollViewFrame.Height - descriptionViewFrame.Height);
_infoView.ScrollView.ContentSize = new SizeF(scrollViewFrame.Size.Width, contentHeight + scrollViewFrame.Height + 20 - descriptionViewFrame.Height);
});
return false;
}
} else if(navigationType == UIWebViewNavigationType.LinkClicked)
{
UIApplication.SharedApplication.OpenUrl(url);
return false;
}
return true;
}
I am completely at a loss. Why would links on the top within view work, and the ones at the bottom where I have to scroll won't?
Any help would be very greatly appreciated.

Related

Xamarin.form Move up view when keyboard appear

I'm trying to build a Chat app UI, the idea of the Layout was pretty simple:
When the input bar is focused, keyboard show up and "push" up the chat bar, as it's a grid, the ListView will resize to fit the screen:
I update the input bar's margin to "push" it up:
NSValue result = (NSValue)args.Notification.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
CGSize keyboardSize = result.RectangleFValue.Size;
if (Element != null){
Element.Margin = new Thickness(0, 0, 0,keyboardSize.Height); //push the entry up to keyboard height when keyboard is activated
}
And this is the result:
https://drive.google.com/file/d/1S9yQ6ks15BRH3hH0j_M8awpDJFRFitUi/view?usp=sharing
The view did push up and the ListView also resized as expected, however there are two issues that I had no idea how to solve it:
How can I retain the ListView scroll position after resize?
Lack of animation to push up the view
I have search over the web, tried IQKeyboardManager and KeyboardOverLap, The push up animation is nice and smooth, however strange things happened:
https://drive.google.com/file/d/1Zm0lMKB3wq07ve67wlcvLuNM_6Waad7R/view?usp=sharing
Instead of resizing the ListView, this approach Push the entire ListView up, that I cannot see the first few items, of course the scroll bar can be scroll out of screen
Extra strange spaces at the bottom of the ListView
Any help will be appreciated, thank you!
Solution:
void OnKeyboardShow(object sender, UIKeyboardEventArgs args)
{
NSValue result = (NSValue)args.Notification.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
CGSize keyboardSize = result.RectangleFValue.Size;
if (Control != null)
{
int bottomMargin = 0;
var sa = UIApplication.SharedApplication.KeyWindow.SafeAreaInsets;
bottomMargin = (int)sa.Bottom;
CGPoint offset = Control.ContentOffset;
var difference = keyboardSize.Height - bottomMargin;
if (Control.ContentSize.Height > Control.Frame.Height)
{
offset.Y += difference;
Control.SetContentOffset(offset, true);
}
else if (Control.ContentSize.Height + keyboardSize.Height > Control.Frame.Height)
{
offset.Y += Control.ContentSize.Height + keyboardSize.Height - Control.Frame.Height - bottomMargin;
Control.SetContentOffset(offset, true);
}
Control.ContentInset = new UIEdgeInsets(0, 0, difference, 0);
Control.ScrollIndicatorInsets = Control.ContentInset;
}
}
void OnKeyboardHide(object sender, UIKeyboardEventArgs args)
{
if (Control != null)
{
Control.ContentInset = new UIEdgeInsets(0, 0, 0, 0);
Control.ScrollIndicatorInsets = new UIEdgeInsets(0, 0, 0, 0);
}
}
Solution:
Refer the following code
in iOS Custom Renderer
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if(Control!=null)
{
Control.KeyboardDismissMode = UIScrollViewKeyboardDismissMode.OnDrag;
NSNotificationCenter.DefaultCenter.AddObserver(this, new Selector("KeyBoardWillShow:"), new NSString("UIKeyboardWillShowNotification"), null);
NSNotificationCenter.DefaultCenter.AddObserver(this, new Selector("KeyBoardWillHide:"), new NSString("UIKeyboardWillHideNotification"), null);
}
}
[Export("KeyBoardWillShow:")]
void KeyBoardWillShow(NSNotification note)
{
NSValue keyboardRect = (NSValue)note.UserInfo.ObjectForKey(new NSString(UIKeyboard.FrameEndUserInfoKey));
Control.ContentInset = new UIEdgeInsets(0,0, keyboardRect.RectangleFValue.Size.Height,0);
}
[Export("KeyBoardWillHide:")]
void KeyBoardWillHide(NSNotification note)
{
Control.ContentInset = UIEdgeInsets.Zero;
}

Scrollview of whole page is not working when I added Tap to zoom view(Image) functionality in xamarin iOS

Scrollview of whole page is not working when I added Tap to zoom view(Image) functionality in xamarin iOS.
I have two scrollview-one is using for tap to zoom functionality and another scroll-scroll2 which is used to scroll the whole details page.This scrooll2 is not working but Tap to Zoom is working fine.
scrollView = new UIScrollView(
new CGRect(0, 80,View.Frame.Width
, View.Frame.Height -200));
View.AddSubview(scrollView);
scroll2.ScrollEnabled = true;
scroll2.ContentSize = new CGSize(0f, 1760f);
scrollView.ScrollEnabled = false;
ImageService.Instance.LoadUrl(GlobalVar.imgpath + travelerlistdetail.orderDetails.product_image)
.Retry(3, 200)
.DownSample(100, 100)
.Into(ImgProd);
//scrollView.ContentSize = ImgProd.Size;
scrollView.AddSubview(ImgProd);
scrollView.MinimumZoomScale = 0.9f;
scrollView.MaximumZoomScale = 3f;
//scrollView.PinchGestureRecognizer.Enabled = false;
//await Task.Delay(2000);
//scrollView.ContentSize = ImgProd.Image.Size;
scrollView.ViewForZoomingInScrollView += (UIScrollView sv) => { return ImgProd; };
UITapGestureRecognizer doubletap = new UITapGestureRecognizer(OnDoubleTap)
{
NumberOfTapsRequired = 1 // double tap
};
scrollView.AddGestureRecognizer(doubletap);
private void OnDoubleTap(UIGestureRecognizer gesture)
{
scroll2.ScrollEnabled = false;
scrollView.ScrollEnabled = true;
if (scrollView.ZoomScale > 1)
scrollView.SetZoomScale(0.25f, true);
else
scrollView.SetZoomScale(3f, true);
}
scroll2 is total page's scroll and scrollview is the imageview scroll when zoom in

Xamarin iOS - Fit scrollview's contents within the scrollview

This may be a fairly simple solution but none of the suggestions here work for me. I have a UIScrollView inside a UITableViewCell. I'm adding dynamic images from a list into the scrollview like this.
public void InitViews()
{
scrollViewThumbNails.ContentSize =
new SizeF((float)scrollViewThumbNails.Frame.Size.Width/listCount * listCount,
(float)scrollViewThumbNails.Frame.Size.Height);
for (int i = 0; i < listCount; i++)
{
var imageView = new UIImageView
{
Frame = new RectangleF((float)i * (float)scrollViewThumbNails.Frame.Size.Width / listCount, 0,
(float)scrollViewThumbNails.Frame.Size.Width / listCount, (float)scrollViewThumbNails.Frame.Size.Height),
UserInteractionEnabled = true,
ContentMode = UIViewContentMode.ScaleAspectFit
};
//call method to load the images
var index = i;
imageView.SetImage(
url: new NSUrl(allItems[i].AbsoluteUri),
placeholder: UIImage.FromFile("placeholder.png"),
completedBlock: (image, error, type, url) =>
{
//when download completes add it to the list
if (image != null)
{
allImages.Add(image);
scrollViewThumbNails.AddSubview(imageView);
}
});
}
I found a suggestion here which advices to set the content size of the scrollview, based on the total number of subviews in ViewDidAppear like this:
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
CGRect contentRect = CGRect.Empty;
foreach(UIImageView view in scrollViewThumbNails.Subviews)
{
contentRect = CGRect.Union(contentRect,view.Frame);
}
scrollViewThumbNails.ContentSize = contentRect.Size;
}
The imageViews are still spaced out and do not start from the edge of the screen/scrollview as shown in my screen shot below. I would like for the first image to always be positioned at the origin of the scrollview and wouldn't want the spacing between each image. How can I adjust the scrollview based on the size of its contents?
Can someone show me what I'm missing? Thanks.
I found a solution here where I calculate the total height and width of all the views and assign that as the content size of the scroll view in ViewDidLayoutSubviews:
Solution
public override void ViewDidLayoutSubviews ()
{
base.ViewDidLayoutSubviews ();
try{
float scrollViewHeight = 0.0f;
float scrollViewWidth = 0.0f;
foreach(UIImageView view in scrollViewThumbnails.Subviews)
{
scrollViewWidth += (float)view.Frame.Size.Width;
scrollViewHeight += (float)view.Frame.Size.Height;
}
scrollViewThumbnails.ContentSize = new CGSize(scrollViewWidth, scrollViewHeight);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message+ex.StackTrace);
}
}

How to check whether a web page has been rendered completely or not?

I am making an iOS app, one page of my app has a webview and multiple views follows. In this page, the webview should auto expand and the height should fit with the content injected dynamically, because i do not want the webview has scroll. Now, i have a problem, i can't post the exact height of doucment.body to the client to set webview's height, because i can't confirm when the whole content has been render into the document.
i have tried to check the height change of document.body,till the height is not changed, i post '0'. The client get the signal i post through invokeNative('changeHeight', params),when it is 0, the client get the document.body height, but in my test, that is not the exact height of the document. Just part of the content shows.
function reportHeight() {
var preH = document.body.offsetHeight;
var params = {
"heigh": preH
};
invokeNative('changeHeight', params); //the method i communicate with client
var timeId = setInterval(function() {
var height = document.body.offsetHeight;
if((height - preH) == 0) {
invokeNative('changeHeight', {"heigh": 0});
clearInterval(timeId);
setTimeout(function() {
invokeNative('changeHeight', {"heigh": 10});
}, 500)
return false;
}
var params = {
"heigh": (height - preH)
};
preH = height;
invokeNative('changeHeight', params);
}, 30);
}
is there any way to fix this problem?
By the way, the content injected into the page only has css and html tags, and there is no js.

Xamarin iOS - Multiple dynamically sized UIWebViews

I'm having an issue asynchronously loading several UIWebViews into one Section. I'm using MonoTouch.Dialog to generate the UI. I am loading data from a blog and showing 10 items which consist of an image plus some HTML text. What's happening is that the posts are not all showing up and the ones that are showing up are out of order. Here's what I'm doing:
public partial class BlogViewController : DialogViewController
{
private Section mainSection;
public BlogViewController () : base (UITableViewStyle.Grouped, null)
{
Root = new RootElement ("");
mainSection = new Section ("");
mainSection.Add(new ActivityElement ());
Root.Add (mainSection);
}
public override void ViewDidLoad()
{
base.ViewDidLoad ();
new Thread (new ThreadStart(PopulateBlog)).Start ();
}
private void PopulateBlog ()
{
var posts = service.GetPosts (currentOffset, 10);
InvokeOnMainThread (delegate {
foreach (var post in posts) {
//grab an appropriate image size
var altSize = post.photos [0].alt_sizes.Where (x => x.width < 401).OrderByDescending(x => x.width).FirstOrDefault ();
if (altSize != null) {
var img = LoadImageFromUri(altSize.url);
//scale the image, not really important
var imageView = new UIImageView (new RectangleF (0, 0, screenWidth, height));
imageView.Image = img;
var content = new UIWebView ();
//When the HTML finishes rendering figure out the size and add it to the section. Apparently can't figure the size ahead of time?
content.LoadFinished += (sender, e) =>
{
var contentHeight = Int32.Parse (content.EvaluateJavascript ("document.getElementById('content').offsetHeight;"));
content.Frame = new RectangleF (0, height + 10, screenWidth, contentHeight + 10);
//dynamically size this view to fit the content
var view = new UIView
(new RectangleF (0, 0,
screenWidth,
height + contentHeight));
view.AddSubview (content);
view.AddSubview (imageView);
//add the view to the Section which is later added to the Root
mainSection.Add(view);
};
var htmlString = #"some HTML here";
content.LoadHtmlString(someHtml);
content.ScrollView.ScrollEnabled = false;
content.ScrollView.Bounces = false;
}
}
});
Root.Reload(mainSection, UITableViewRowAnimation.None);
}
}
I'm guessing that A) the LoadFinished events are not happening in the same order that they are queued up, and that B) The Root.Reload gets called before they all fire. I tried spinning with a Thread.Sleep prior to the Root.Reload but then the LoadFinished events never even get fired.
I also tried putting all of the UIView elements in a Dictionary to be added after they are all populated but it seems like as soon as InvokeOnMainThread ends the LoadFinished event handler never gets called again.
Do you have to use MT.Dialog? I would personal try to use UITableView instead. Load the web view content, determine its size (maybe use sizeThatFits?) and push it to the data source at correct location. Then return the size on the UITableViewDelegate's heightForRowAtIndexPath call. UITableView should take care of the rest.

Resources