Table of Contents

FlexView (iOS Native) (C# / Mobile / iOS-Native)

Note

This demo is available in your FlexCel installation at <FlexCel Install Folder>\samples\csharp\VS2026\Mobile\iOS-Native\FlexView and also at https:​//​github.​com/​tmssoftware/​TMS-​FlexCel.​NET-​demos/​tree/​master/​csharp/​VS2026/​Mobile/​iOS-​Native/​Flex​View

Overview

This example shows how to open Excel files (.xls and .xlsx) shared from other iOS apps (such as Mail or Dropbox), convert them to PDF using FlexCel, and display the result with zoom and pan support. You can also export the generated PDF back to other apps or print it.

How to Use

  1. Open another app (e.g., Mail), find an .xls or .xlsx attachment, press Share, and select FlexView.
  2. FlexView will convert the file to PDF and display it in a scrollable, zoomable view.
  3. Use the Share button to export the generated PDF to other apps (Mail, Dropbox, etc.) or to print it.
  4. Use the Randomize button to modify numeric cells with random values, re-convert to PDF, and see the updated result.

FlexCel Features Demonstrated

  • XlsFile: Opening Excel files received from other apps
  • FlexCelPdfExport: Converting Excel workbooks to PDF with full fidelity
  • GetCellValue / SetCellValue: Reading and modifying cell values programmatically
  • Recalc(): Recalculating formulas after modifying cell values

Implementation Details

  • SceneDelegate.cs: Handles the iOS scene lifecycle and file-open URLs. Files can arrive at launch (WillConnect) or while the app is running (OpenUrlContexts). A pendingUrl field defers processing until the view is ready.
  • WebViewer.cs: A UIViewController with a WKWebView that displays the generated PDF. Implements IUIScrollViewDelegate for pinch-to-zoom support (1x to 10x range).
  • File flow: The original Excel file is opened read-only from the sender's sandbox, converted to PDF in the InternetCache folder, and displayed. Because of iOS sandbox restrictions, the original file cannot be modified in place; changes are saved as a new copy.
  • File sharing: The generated PDF is shared via UIDocumentInteractionController, which presents the standard iOS share sheet.

Tutorial

There is a step-by-step tutorial on how this app was created available at https://doc.tmssoftware.com/flexcel/net/tutorials/ios-tutorial.html

Files

AppDelegate.cs

namespace FlexView
{
    [Register("AppDelegate")]
    public class AppDelegate : UIApplicationDelegate
    {
        public override bool FinishedLaunching(UIApplication application, NSDictionary? launchOptions)
        {
            // Override point for customization after application launch.
            return true;
        }

        public override UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options)
        {
            // Called when a new scene session is being created.
            // Use this method to select a configuration to create the new scene with.
            // "Default Configuration" is defined in the Info.plist's 'UISceneConfigurationName' key.
            return new UISceneConfiguration("Default Configuration", connectingSceneSession.Role);
        }

        public override void DidDiscardSceneSessions(UIApplication application, NSSet<UISceneSession> sceneSessions)
        {
            // Called when the user discards a scene session.
            // If any sessions were discarded while the application was not running, this will be called shortly after 'FinishedLaunching'.
            // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
        }
    }
}

Main.cs

using FlexView;

// This is the main entry point of the application.
// If you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main(args, null, typeof(AppDelegate));

SceneDelegate.cs

namespace FlexView
{
    [Register("SceneDelegate")]
    public class SceneDelegate : UIResponder, IUIWindowSceneDelegate
    {

        [Export("window")]
        public UIWindow? Window { get; set; }

        [Export("scene:willConnectToSession:options:")]
        public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions)
        {
            // The storyboard is configured in Info.plist (UISceneStoryboardFile),
            // so the system automatically creates the window and sets the root view controller
            // from Main.storyboard's initial view controller.

            // Handle URL if the app was launched by opening a file
            if (connectionOptions.UrlContexts?.AnyObject is UIOpenUrlContext urlContext)
            {
                pendingUrl = urlContext.Url;
            }
        }

        NSUrl? pendingUrl;

        [Export("sceneDidBecomeActive:")]
        public void DidBecomeActive(UIScene scene)
        {
            // Process any pending URL once the view is fully loaded
            if (pendingUrl != null && Window?.RootViewController is WebViewer webViewer)
            {
                webViewer.Open(pendingUrl);
                pendingUrl = null;
            }
        }

        [Export("scene:openURLContexts:")]
        public void OpenUrlContexts(UIScene scene, NSSet<UIOpenUrlContext> urlContexts)
        {
            // Called when a file is opened while the app is already running
            if (urlContexts?.AnyObject is UIOpenUrlContext urlContext && Window?.RootViewController is WebViewer webViewer)
            {
                webViewer.Open(urlContext.Url);
            }
        }

        [Export("sceneDidDisconnect:")]
        public void DidDisconnect(UIScene scene)
        {
            // Called as the scene is being released by the system.
            // This occurs shortly after the scene enters the background, or when its session is discarded.
            // Release any resources associated with this scene that can be re-created the next time the scene connects.
            // The scene may re-connect later, as its session was not neccessarily discarded (see UIApplicationDelegate `DidDiscardSceneSessions` instead).
        }

        [Export("sceneWillResignActive:")]
        public void WillResignActive(UIScene scene)
        {
            // Called when the scene will move from an active state to an inactive state.
            // This may occur due to temporary interruptions (ex. an incoming phone call).
        }

        [Export("sceneWillEnterForeground:")]
        public void WillEnterForeground(UIScene scene)
        {
            // Called as the scene transitions from the background to the foreground.
            // Use this method to undo the changes made on entering the background.
        }

        [Export("sceneDidEnterBackground:")]
        public void DidEnterBackground(UIScene scene)
        {
            // Called as the scene transitions from the foreground to the background.
            // Use this method to save data, release shared resources, and store enough scene-specific state information
            // to restore the scene back to its current state.
        }
    }
}

WebViewer.cs

using ObjCRuntime;
using WebKit;
using FlexCel.Render;
using FlexCel.XlsAdapter;

namespace FlexView
{
    [Register("WebViewer")]
    public partial class WebViewer(NativeHandle handle) : UIViewController(handle), IUIScrollViewDelegate
    {
        [Outlet]
        public WKWebView Viewer { get; set; } = null!;

        public override void ViewDidLoad()
        {
            base.ViewDidLoad();
            Viewer.ScrollView.MinimumZoomScale = 1.0f;
            Viewer.ScrollView.MaximumZoomScale = 10.0f;
            Viewer.ScrollView.Delegate = this;
            base.ViewDidLoad();
            Viewer.LoadHtmlString("<html><head><style>body{display:flex;justify-content:center;align-items:center;height:100vh;margin:0;text-align:center;font-size:2em;}</style></head>" +
                                  "<body><h1>Please share an xls or xlsx file <br>from other app into FlexView.<br/ >" +
                                  "For help on how to use this example, please read the " +
                                  "<a href=\"https://doc.tmssoftware.com/flexcel/net/tutorials/ios-tutorial.html\">tutorial</a></h1></body></html>", new NSUrl(""));

        }

        [Export("viewForZoomingInScrollView:")]
        public UIView ViewForZoomingInScrollView(UIScrollView scrollView)
        {
            return Viewer.ScrollView.Subviews.FirstOrDefault()!;
        }
            
        NSUrl? XlsUrl;
        string? PdfPath;
        string? XlsPath;


        public bool Open(NSUrl url)
        {
            XlsUrl = url;
            XlsPath = url.Path;
            return Refresh();
        }
        private bool Refresh()
        {
            try
            {
                RemoveOldPdf();
                if (XlsPath == null) throw new Exception("No url provided");
                XlsFile xls = new(XlsPath);
                PdfPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.InternetCache), Path.ChangeExtension(Path.GetFileName(XlsPath), ".pdf")!);

                using (FlexCelPdfExport pdf
                             = new(xls, true))
                {
                    using FileStream fs = new(PdfPath,
                           FileMode.Create);
                    pdf.Export(fs);
                }
                Viewer.LoadRequest(new
                    NSUrlRequest(NSUrl.FromFilename(PdfPath)));
            }
            catch (Exception ex)
            {
                Viewer.LoadHtmlString("<html>Error opening " +
                    System.Security.SecurityElement.Escape(
                        Path.GetFileName(XlsUrl?.Path))
                    + "<br><br>"
                    + System.Security.SecurityElement.Escape(
                       ex.Message)
                    + "</html>", new NSUrl(""));

                return false;
            }
            return true;
        }

        [Export("randomizeTapped:")]
        public void RandomizeTapped(NSObject sender)
        {
            if (XlsUrl == null || sender == null)
            {
                ShowHowToUse();
                return;
            }


            XlsFile xls = new(XlsPath, true);
            //We'll go through all the numeric cells and make them random
            Random rnd = new();

            for (int row = 1; row <= xls.RowCount; row++)
            {
                for (int colIndex = 1;
                     colIndex < xls.ColCountInRow(row);
                     colIndex++)
                {
                    int XF = -1;
                    object val = xls.GetCellValueIndexed(row, colIndex, ref XF);
                    if (val is double) xls.SetCellValue(row,
                              xls.ColFromIndex(row, colIndex), rnd.Next());
                }
            }

            //We can't save to the original file, we don't have permissions.
            XlsPath = Path.Combine(
                Environment.GetFolderPath(
                Environment.SpecialFolder.InternetCache),
                "tmpFlexCel" + Path.GetExtension(XlsUrl.Path));

            xls.Save(XlsPath);
            Refresh();
        }
        [Export("shareTapped:")]
        public void ShareTapped(NSObject sender)
        {
            if (PdfPath == null)
            {
                ShowHowToUse(); 
                return;
            }
            UIDocumentInteractionController docController = new()
            {
                Url = NSUrl.FromFilename(PdfPath)
            };
            docController.PresentOptionsMenu((UIBarButtonItem)sender, true);

        }

        private void RemoveOldPdf()
        {
            if (PdfPath != null)
            {
                try
                {
                    File.Delete(PdfPath);
                }
                catch
                {
                    //do nothing, this was just a
                    //cache that will get deleted anyway.
                }
                PdfPath = null;
            }
        }

        void ShowHowToUse()
        {
            using var alert = UIAlertController.Create("Please open FlexView from other Application.",
                "In order to use this example you need to go to another app"
                + " like dropbox or mail, and share an xls or xlsx file with FlexView."
                + " When you click \"Share\" and select \"FlexView\" in the other app,"
                + " the file will be converted to pdf and previewed with FlexView.", UIAlertControllerStyle.Alert);
            alert.AddAction(UIAlertAction.Create("Ok", UIAlertActionStyle.Cancel, null));
            PresentViewController(alert, animated: true, completionHandler: null);
        }


    }
}