Issue Presenting TEditor from "modal" Xamarin Forms Page (iOS)
bie5865 opened this issue · 10 comments
I'm able to present the TE control fine via a Xamarin Forms Page that was that was "Pushed" on the Navigation stack. However, I run into issues if the page that I'm trying to present the control from was a modal, the control will not present. The app doesn't crash either. I've tried both the sample application as well as consuming the nuget package in a simple application.
Steps to Reproduce:
- Download Sample.
- In the App class switch the MainPage to Present a new page.
public class App : Application
{
public App()
{
this.MainPage = new NavigationPage(new SecondPage());
}
- Have the second page present a ContentPage with that will present the TEditor Control.
public SecondPage()
{
var nextButton = new Button { Text = "Next Page" };
Content = new StackLayout
{
Children = { nextButton}
};
var contentPage = new ContentPage { Content = new TEditorHtmlView(), BackgroundColor = Color.White };
nextButton.Clicked += (object sender, EventArgs e) => { Navigation.PushModalAsync(contentPage); };
}
- The TEditor Control will not present
Also, the control will not present if Xamarin forms main page is a Master Detail. I'm taking a look at the TEditorImplementation class in the iOS project. I think I might have to check for instances where there is no UINavigationController present.
Please follow the sample code in repo, TEditor cannot be used in that way "new ContentPage { Content = new TEditorHtmlView(), BackgroundColor = Color.White };"
TEditor should be used in a Navigation Page, please make sure your Detail page is Navigation page.
The master detail problem can be fixed with this code in the iOS project. You'll need to add the Xamarin.Forms Package.
public class TEditorImplementation : BaseTEditor
{
public override Task<string> ShowTEditor(string html, ToolbarBuilder toolbarBuilder = null)
{
TaskCompletionSource<string> taskRes = new TaskCompletionSource<string>();
var tvc = new TEditorViewController();
tvc.Title = Title;
ToolbarBuilder builder = toolbarBuilder;
if (toolbarBuilder == null)
builder = new ToolbarBuilder().AddAll();
tvc.BuildToolbar(builder);
tvc.SetHTML(html);
UINavigationController nav = null;
foreach (var vc in UIApplication.SharedApplication.Windows[0].RootViewController.ChildViewControllers) {
if (vc is UINavigationController) {
nav = (UINavigationController)vc;
break;
} else if (vc is PhoneMasterDetailRenderer) {
PhoneMasterDetailRenderer renderer = vc as PhoneMasterDetailRenderer;
nav = findNavControllerRecursive(renderer.ViewController.ChildViewControllers);
if (nav != null) {
break;
}
break;
} else if (vc is TabletMasterDetailRenderer) {
TabletMasterDetailRenderer renderer = vc as TabletMasterDetailRenderer;
nav = findNavControllerRecursive(renderer.ViewController.ChildViewControllers);
if (nav != null) {
break;
}
break;
}
}
if (nav != null) {
tvc.NavigationItem.SetRightBarButtonItem(new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, async (item, args) => {
nav.PopViewController(true);
taskRes.SetResult(await tvc.GetHTML());
}), true);
nav.PushViewController(tvc, true);
}
return taskRes.Task;
}
UINavigationController findNavControllerRecursive(UIViewController[] input) {
UINavigationController nav = null;
foreach (UIViewController each in input) {
if (each is UINavigationController) {
nav = each as UINavigationController;
break;
}
nav = findNavControllerRecursive(each.ChildViewControllers);
}
return nav;
}
}
Thank you thank you, that worked perfect!
Hi! I'm trying to apply the solution exposed by @swansca79 but when my plc calls TEditor.CrossTEditor.Current.ShowTEditor it seems that it's not firing the ios overrided one... so the editor never shows up in IOS... Any clue? Detailed example?
How did you achieved it @bie5865 ?
Thanks!
I'm having a similar problem where I call this new overrided function in my iOS project using the Xamarin Dependency service, and it still doesn't show up. The function call never returns, but an error is not thrown. It's like the Thread just exits or something. The editor shows up in the sample app though. I don't see what could be different unless there is an issue with executing on the UIThread.
Here is where I'm pushing the new page on the stack:
await Navigation.PushAsync(new ContentPage { Content = new InterviewRichTextEditor() });
Here is my code Xamarin.Forms StackLayout class that is calling the iOS specific things. It's this first line that is never being returned from:
`
string html = await DependencyService.Get<IRichTextEditor>().CallShowTEditor("<!-- This is an HTML comment --><p>This is a test of the <strong>TEditor</strong> by <a title=\"XAM consulting\" href=\"http://www.xam-consulting.com\">XAM consulting</a></p>");
if (!string.IsNullOrEmpty(html))
{
_displayWebView.Source = new HtmlWebViewSource() { Html = html };
}
`
Here is my interface:
`
public interface IRichTextEditor
{
Task<string> CallShowTEditor(string html, ToolbarBuilder toolbarBuilder = null);
}
`
Here is my implementation in the iOS project:
`
[assembly: Xamarin.Forms.Dependency(typeof(iOSRichTextEditor))]
namespace Kindred.IntegriTrack.Mobile.iOS
{
public class iOSRichTextEditor : BaseTEditor, IRichTextEditor
{
public iOSRichTextEditor() { }
public Task<string> CallShowTEditor(string html, ToolbarBuilder toolbarBuilder = null)
{
Task<string> temp = null;
Xamarin.Forms.Device.BeginInvokeOnMainThread( () => {
temp = ShowTEditor(html, toolbarBuilder);
});
return temp;
}
public override Task<string> ShowTEditor(string html, ToolbarBuilder toolbarBuilder = null)
{
TaskCompletionSource<string> taskRes = new TaskCompletionSource<string>();
var tvc = new TEditorViewController();
tvc.Title = "what";
ToolbarBuilder builder = toolbarBuilder;
if (toolbarBuilder == null)
builder = new ToolbarBuilder().AddAll();
tvc.BuildToolbar(builder);
tvc.SetHTML(html);
UINavigationController nav = null;
foreach (var vc in UIApplication.SharedApplication.Windows[0].RootViewController.ChildViewControllers)
{
if (vc is UINavigationController)
{
nav = (UINavigationController)vc;
break;
}
else if (vc is PhoneMasterDetailRenderer)
{
PhoneMasterDetailRenderer renderer = vc as PhoneMasterDetailRenderer;
nav = findNavControllerRecursive(renderer.ViewController.ChildViewControllers);
if (nav != null)
{
break;
}
break;
}
else if (vc is TabletMasterDetailRenderer)
{
TabletMasterDetailRenderer renderer = vc as TabletMasterDetailRenderer;
nav = findNavControllerRecursive(renderer.ViewController.ChildViewControllers);
if (nav != null)
{
break;
}
break;
}
}
if (nav != null)
{
tvc.NavigationItem.SetRightBarButtonItem(new UIBarButtonItem("Done", UIBarButtonItemStyle.Done, async (item, args) => {
nav.PopViewController(true);
taskRes.SetResult(await tvc.GetHTML());
}), true);
nav.PushViewController(tvc, true);
}
return taskRes.Task;
}
UINavigationController findNavControllerRecursive(UIViewController[] input)
{
UINavigationController nav = null;
foreach (UIViewController each in input)
{
if (each is UINavigationController)
{
nav = each as UINavigationController;
break;
}
nav = findNavControllerRecursive(each.ChildViewControllers);
}
return nav;
}
}
}
`
I'm not sure if you can override the method in that manner when the source is in a nuget package. I think it has to do with the bait and switch technique used for most Xamarin packages. I made several changes to the source and created my own package that I use locally. I would assume @bie5865 did the same.
I'm not convinced that the override function really addresses the problem I'm having, since a separate Xamarin Forms application ran fine with the TEditor. Are there different Xamarin Forms startup options that I should be aware of? For example we are starting the App with a NavigationPage:
MainPage = new NavigationPage(new LogInPage());
There are other options, like a TabbedPage or a MasterDetailPage, that's what my code snippet is fixing. Since you are using NavigationPage, you shouldn't need to change anything. The original problem for this issue was launching the editor from a Modal page, so just make sure you aren't doing that.
Would it be possible to send you a zipped up simple project where I have been able to replicate the behavior? The whole project is just two pages, with a MastersDetail page, and each page is getting called via the MD page. The behavior of the editor not showing up and the thread just dying exists.