作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Demir Selmanovic的头像

Demir Selmanovic

Demir是一名开发人员和项目经理,在广泛的软件开发角色方面拥有超过15年的专业经验.

Years of Experience

24

Share

Once upon a time, 有一家公司拥有所有最好的工具, 为他们的平台编写软件真是太棒了. 但慢慢地,他们对自己的问题变得漠不关心了. 当他们的系统崩溃时,他们并没有惊慌, 而是接受宇宙的这种状态作为生命的事实. 他们相信他们的程序本身是完美的, serene and elegant, 他们的目的不言而喻.

天啊,要是他们知道自己错得有多离谱就好了……

当他们意识到自己的错误,他们的首席执行官大声疾呼的时候,这早就该发生了 把所有的开发者都带回来 谁离开了他们的平台扬帆而去. The company was Microsoft and I, for one, 确信他们的命运已经注定,他们将缓慢但肯定地从技术领域的最前沿消失吗.

我很高兴我错了!

在过去的几年里,微软已经从他们的袖子里抽出了一些王牌. Yes, 他们搞砸了Skype(我现在还讨厌他们), 智能手机失败, 而且几乎在平板电脑上取得了成功. 但是,他们也做了一些非常了不起的事情. 他们放弃了封闭的帝国模式,开始开源 .NET, 加入Linux基金会, SQL Server for Linux版本发布, 创造了这个伟大的新工具,叫做 Visual Studio for Mac.

That’s right, a real 微软的IDE不是用于Windows,而是用于Mac. Imagine that!

Visual Studio For Mac

在Mac上使用c#编写首个跨平台Android和iOS应用程序

你可以使用Mac版的Visual Studio创建几乎任何类型的应用程序. 这意味着你可以使用c#开发iOS, tvOS, Android, Mac, .. NET Core,甚至是ASP.NET. 因为现在所有的酷孩子都在写手机应用, 让我们看看如何在Mac的Visual Studio中使用c#创建一个可以在Android和iOS上运行的移动应用程序.

您需要做的第一件事是选择应用程序模板. 让我们从一个简单的“单视图应用程序”开始.”

Single view app.

在填入包名并启动应用程序之后, Visual Studio将创建一个包含三个项目的解决方案. 第一个项目将是一个共享库,您应该在其中保留与平台无关的代码, 另外两个将是安卓和iOS应用.

Getting Started.

你可以使用“运行”菜单或应用程序栏中的命令来启动你的应用程序.

你好,世界,点击我!

Logging two clicks.

Congratulations! 你现在是一名iOS和Android开发者,尽管你从未写过一行Objective-C, Swift, or Java code.

不过,我们的c# iPhone应用程序还没有真正完成很多工作. 让我们把事情变得更有趣,并将地图和位置服务结合起来.

使用地图和位置服务

请记住,Mac版VS还处于“预览”阶段,没有太多的帮助和文档,你会发现如何使用它. 关于如何做事情的最好参考仍然是官方Xamarin文档.

Visual Studio For Mac不使用与PC上看到的Xamarin工具相同的解决方案和应用程序结构. 在大多数情况下,您需要进行实验并克服一些障碍才能使他们的示例工作. 让我们希望微软能在他们的游戏中保持领先,并在Mac版VS的最终版本发布后提供一个令人敬畏的MSDN资源集合.

在iOS上显示当前位置

访问移动设备资源, 例如当前位置, 要求用户“手动”授予应用程序使用这些资源的权限. iOS uses the file info.plist 要存储这些设置. VS for Mac提供了一个可视化的界面来编辑这个文件. 我们需要做的第一件事是为名为 NSLocationWhenInUseUsageDescription.

在使用描述中增加位置的值.

Note: 当你设置属性名时,VS会为" NSLocationWhenInUseUsageDescription "显示一个长名称. 这是预料之中的,不用担心.

我们的引导应用程序是用一个简单的按钮创建的,该按钮计算点击次数. 您要做的第一件事是删除它,并用地图替换屏幕内容. 为了做到这一点,寻找 Main.storyboard 文件,然后双击它,在编辑器中打开它.

故事板是应用程序用户界面的可视化表示, 显示内容的屏幕以及这些屏幕之间的连接. 故事板是由一系列场景组成的, each of which represents a view controller and its views; scenes are connected by segue objects, 哪个表示两个视图控制器之间的转换.

故事板是由Apple引入的,Xamarin也采用了故事板. Refer to Apple Documentation or Xamarin的文档 for more information.

删除按钮并向页面添加Map View组件.

移除地图视图组件.

确保正确命名“mapView”组件.

命名地图视图组件

现在剩下的就是清理你的 ViewController.cs file, and modify the ViewDidLoad() 方法匹配如下:

        using CoreLocation;

        ViewDidLoad()
        {
            base.ViewDidLoad();
            //加载视图后执行任何额外的设置,通常是从nib.

            CLLocationManager = new CLLocationManager();
            locationManager.RequestWhenInUseAuthorization ();
            mapView.ShowsUserLocation = true;
        }

你可以使用“快速修复”功能让VS自动添加对CoreLocation库的引用,也可以手动添加.

运行iOS应用程序后,您应该会看到访问您的位置的请求. 一旦获得许可,你的地图将加载一个标准的蓝点,显示你在哪里(或你假装使用iOS模拟器:))。.

允许在应用程序中使用位置.

在Android上显示当前位置

Unfortunately, 谷歌和微软决定让这个简单的任务变得比iOS更复杂一些. 为了在Android应用程序中使用地图, 您将需要创建谷歌地图API密钥, and add it to your AndroidManifest.xml file.

Xamarin创建了一个非常简单的指南 获取Google Maps API密钥. 在继续之前,请按照指南中的步骤操作. 当你完成后,你的 AndroidManifest.xml 应该包含这样的设置:


现在可以向应用程序添加映射了.

Mac版VS的伟大之处在于它是由NuGet驱动的,就像它的老大哥一样. 由于默认情况下不包含映射处理库,因此您需要安装 Xamarin.Forms.Maps package.

Install Xamarin.Forms.Maps

然而,没有“地图视图”组件,你可以直接拖动到你的“活动”. Instead, adding a map to the screen requires manually changing your Resources->layout->Main.axml file. 可以使用设计器视图删除之前创建的按钮, 然后切换到“代码视图”,并将以下片段代码添加到您的 LinearLayout:

    

与面向iOS的c#一样,你需要配置你的应用程序来请求适当的权限. To do so, open AndroidManifest.xml 进行编辑,并按编辑器左下角的“应用程式”按钮. VS将显示一个用于设置这些值的可视化界面. 您需要启用其中的一些,如下所示.

Enabling permissions.

现在是时候编写一些真正的代码了. Find the MainActivity.cs 文件,打开编辑,并做以下修改:

添加命名空间引用:

using Android.Gms.Maps.Model;
using Android.Gms.Maps;
using Android.Locations;

让你的MainActivity也成为一个ILocationListener.

公共类MainActivity: Activity, ILocationListener

在MainActivity中实现ILocationListener方法:
        public void OnProviderEnabled(string provider) {}

        public void OnProviderDisabled(string provider) {}

        公共void OnStatusChanged(字符串提供商,可用性状态,Bundle额外){}

        OnLocationChanged(Android ..Locations.Location location)
        {
            LatLng = new LatLng(location).Latitude, location.Longitude);
            CameraPosition.Builder Builder = camerposition.InvokeBuilder();
            builder.Target(latLng);
            builder.Zoom(15);
            builder.Bearing(155);
            builder.Tilt(10);
            camerposition = builder.Build();
            camerauupdate = CameraUpdateFactory.NewCameraPosition (cameraPosition);

            MapFragment mapFrag = (MapFragment)FragmentManager.FindFragmentById(资源.Id.map);
            GoogleMap = mapFrag.Map;
            if (map != null)
            {
                map.MoveCamera (cameraUpdate);
            }
          }

添加以下两个变量作为类级别变量:

        LocationManager locMgr;
        字符串locationProvider;

And cleanup the OnCreate() 方法看起来像这样:

        OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate (savedInstanceState);


            //从"main"布局资源设置视图
            SetContentView(资源.Layout.Main);

            locMgr = GetSystemService(LocationService)作为LocationManager;

            locationCriteria = new Criteria();

            locationCriteria.Accuracy = Accuracy.Coarse;
            locationCriteria.PowerRequirement =功率.Medium;

            locationProvider = locMgr.GetBestProvider (locationCriteria,真实);
            locMgr.RequestLocationUpdates(locationProvider, 2000,1, this);
        }

方法中调用GetSystemService OnCreate() method, your MainActivity 会被激活吗 ILocationListener 从而能够处理上面列出的所有事件.

运行你的Android应用程序,你应该得到定位到你的位置的地图, 如下图所示.

位于萨拉热窝的地图.

为iOS和Android使用共享库

Mac版VS最大的特点之一就是可以在iOS和Android应用程序之间共享代码. Ideally, 我们可以在一个共享库中拥有应用程序的所有业务逻辑, 限制任何iOS和Android特定代码成为UI的一部分.

让我们创建一个共享类,它将异步执行HTTP请求并在调试控制台中显示内容.

在共享库中创建一个新的类文件 RestClient.cs 使用以下代码:

(确保使用项目中的正确名称空间)

using System;
using System.Net;

namespace testshared
{
    public delegate void callback(string responseText);

    class ReqState
    {
        返回HttpWebRequest的请求状态。
        {
            request = req;
            callback = cb;
        }
        public HttpWebRequest request { get; set; }
        公共回调;
    }

    公共类RestClient
    {
        public RestClient() {}


        getchpage (string url, callback cb)
        {
            HttpWebRequest请求= (HttpWebRequest)WebRequest.Create(url);
            request.BeginGetResponse(new AsyncCallback(FinishWebRequest), new ReqState(request, cb));
        }

        private void FinishWebRequest(IAsyncResult result)
        {
            ReqState = (result ..AsyncState作为ReqState);
            HttpWebResponse response = reqState.request.EndGetResponse(result)作为HttpWebResponse;
            使用(var reader = new System.IO.StreamReader(响应.GetResponseStream ()))
            {
                responseText = reader.ReadToEnd();
                reqState.回调(responseText);
            }
        }

    }
}

在iOS上使用库

Modify your ViewController.cs 文件在iOS项目中匹配以下代码:

(确保使用项目中的正确名称空间)

using System;
using UIKit;
using System.Diagnostics;

namespace testshared.iOS
{
    公共部分类ViewController: UIViewController
    {
        RestClient rest = new RestClient();

        public ViewController(IntPtr handle): base(handle) {}


        ViewDidLoad()
        {
            base.ViewDidLoad();

            //加载视图后执行任何额外的设置,通常是从nib.
            Button.accessbilityidentifier = "myButton";
            Button.TouchUpInside += delegate
            {
                Button.SetTitle("Loading...", UIControlState.Normal);
                rest.FetchPage (" http://www.google.com", doneCallback);
            };
        }

        public void doneCallback(字符串内容)
        {
            InvokeOnMainThread(() =>
            {
                Debug.Write(content);
                Button.SetTitle("All Done", UIControlState.Normal);
            });
        }

        didreceivemmemorywarning ()
        {
            base.DidReceiveMemoryWarning ();
            //释放所有未被使用的缓存数据、图像等.        
        }
    }
}

运行你的iOS应用程序,点击按钮并在Visual Studio中选择“应用程序输出”选项卡. 它应该显示如下:

应用程序输出选项卡.

在Android上使用图书馆

Android应用所需要的改变与iOS应用所需要的改变非常相似. Modify the MainActivity.cs 文件匹配以下内容:

(确保使用项目中的正确名称空间)

using Android.App;
using Android.Widget;
using Android.OS;

namespace testshared.Droid
{
    [Activity(Label = "testshared", MainLauncher = true, Icon = "@mipmap/ Icon ")]
    类MainActivity: Activity
    {
        RestClient rest = new RestClient();

        OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate (savedInstanceState);

            //从"main"布局资源设置视图
            SetContentView(资源.Layout.Main);

            //从布局资源中获取按钮,
            //并为其附加一个事件
            Button button = FindViewById

Note: 两个平台的系统架构, Android and iOS, 要求所有UI交互都发生在主应用程序线程上. 这意味着对UI元素的任何更改也应该在主线程中发生. That is where RunOnUiThread and InvokeOnMainThread come in. 因为HTTP请求是在一个单独的线程中执行的 doneCallback() 在主线程之外被调用, 我们必须使用这些方法才能访问按钮并更改标签.

c#开发者正在占领Android和iOS平台

Mac版的Visual Studio仍有一些问题需要解决, 但乍一看, 我对它的未来感到非常兴奋. 对移动应用程序的需求与日俱增, 使用Mac版的Visual Studio, 微软进一步建立了一支 great C# developers to fill in this need. Naturally, 这并不意味着VS, Xamarin, c# iOS开发将主导移动开发, 但是看到更多的人才, competition, 任何软件开发领域的创新都不是一件坏事.

Swift和Java/JVM现在有一个新的, and very strong, 我们移动设备开发环境的竞争对手.
就这一主题咨询作者或专家.
Schedule a call
Demir Selmanovic的头像
Demir Selmanovic

Located in 萨拉热窝,波斯尼亚-黑塞哥维那联邦,波斯尼亚-黑塞哥维那

Member since July 8, 2014

About the author

Demir是一名开发人员和项目经理,在广泛的软件开发角色方面拥有超过15年的专业经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Years of Experience

24

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.