iOS ScrollView w/contentView (UIView) doesn't display content (using Cirrious FluentLayouts) - c#

I'm not very familiar with scroll views on iOS so please bear with me. I'm using Visual Studio 2019 Preview. I have a view on the main storyboard that has a banner at the top and a label under that. I then put a scroll view under the label as I do not want the banner and label to scroll off the screen. Then programmatically I add a UIView called contentView to the scroll view after adding two controls to the content view, a label called lblUsername and a text view called txtUsername, I set the text of the label to "Enter Username". I only added these two things to the content view to keep things simple until I could better understand how this all works. The code compiles with no errors, but when I run it on my test device, nothing appears other than the banner and the label I placed under that. My code:
public partial class createAccount : UIViewController
{
public User MyUser;
public createAccount (IntPtr handle) : base (handle)
{
}
public override void ViewDidLoad()
{
viewCreateAccount.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
var height = this.NavigationController.NavigationBar.Bounds.Height;
UIView contentView = new UIView();
UILabel lblUsername = new UILabel();
UITextView txtUsername = new UITextView();
lblUsername.Text = "Enter Username: ";
contentView.AddSubviews(lblUsername);
contentView.AddSubviews(txtUsername);
viewCreateAccount.AddConstraints
(
banner.AtTopOf(View, height),
banner.AtRightOf(View, 0),
banner.AtLeftOf(View, 0),
lblTitle.Below(banner, 0),
lblTitle.WithSameWidth(banner)
);
scrollView.Add(contentView);
scrollView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
scrollView.AddConstraints(contentView.FullWidthOf(scrollView));
contentView.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
contentView.AddConstraints(
lblUsername.AtTopOf(contentView,0),
lblUsername.AtLeftOf(contentView,0),
txtUsername.ToRightOf(lblUsername,2)
);
}
}
What am I missing??

I have tested in Version 16.6.1 of VS , it seems that UITextView can not shows when using Cirrious.FluentLayout .Maybe it's an issue or limitation of Cirrious.FluentLayout , you can connect to author to reprot this .
If modify the UITextView to UITextField , then it will show .
...
UIView contentView = new UIView();
UILabel lblUsername = new UILabel();
UITextField txtUsername = new UITextField();
lblUsername.Text = "Enter Username: ";
contentView.AddSubviews(lblUsername);
contentView.AddSubviews(txtUsername);
txtUsername.Text = "Enter UITextField ";
txtUsername.TextColor = UIColor.Black;
txtUsername.BackgroundColor = UIColor.Yellow;
...
The effect (here add a background color for scollview):

Related

Xamarin calculating a stack layout height at run time

I am trying to create a dynamic view, where I get the data from the backend and create views for it on my Xamarin App.
In the XAML view I have a simple stack layout
<StackLayout x:Name="Container">
</StackLayout>
and I am creating views as soon as the date is retrieved as so
Label label = new Label();
label.Text= Text;
label.LineHeight = 1.1;
Container.Children.Add(CreateLabel(label));
The problem is the view doesn't expand to fit all the elements added
calculating the height and setting it as the HeightRequest for stack layout didn't work
any idea or suggestion would be nice.
I made a demo for you by writing the MainPage.xaml.cs file.
public partial class MainPage : ContentPage
{
String Text1 = "This is a text. This is a text.This is a text.This is a text.";
Double LineHeight1 = 1.78;
public MainPage()
{
InitializeComponent();
var layout = new StackLayout { };
var label = new Label { Text = Text1, TextColor = Color.Black, FontSize = 20, LineHeight = LineHeight1, BackgroundColor = Color.Red, LineBreakMode = LineBreakMode.WordWrap };
layout.Children.Add(label);
this.Content = layout;
}
}
You can see that I create a StackLayout and a label and I bind the layout to the content. It works well and you can try it.

Change status bar colour on iOS13

Before iOS 13 I could change the status bar colour using the following bit of code:
UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
{
statusBar.BackgroundColor = UIColor.Clear.FromHex(0x323232);
statusBar.TintColor = UIColor.White;
app.StatusBarStyle = UIStatusBarStyle.BlackOpaque;
}
However, on iOS13 I get the following runtime error
Objective-C exception thrown. Name: NSInternalInconsistencyException Reason: App called -statusBar or -statusBarWindow on UIApplication: this code must be changed as there's no longer a status bar or status bar window. Use the statusBarManager object on the window scene instead.
Any idea on how to change the status bar on iOS13?
EDIT: Just to point out, this is for Xamarin and not for Swift. To clarify the duplicate marker.
From error , you need to use UIStatusBarManager in IOS 13.
If you have updated VS to the latest version(Visual Studio 2019 version 16.3.0/Visual Studio 2019 for Mac version 8.3 above), you can change color as follow:
UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
statusBar.BackgroundColor = UIColor.Yellow;
UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
Else the follow methods can make it works .
UIView statusBar = new UIView(UIApplication.SharedApplication.StatusBarFrame);
statusBar.BackgroundColor = UIColor.Yellow;
UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
If the view is not fully rendered , UIApplication.SharedApplication.KeyWindow will return null .So you can change status bar color after fully rendered . Here is the sample .
===================================Update=================================
If in Forms project , you can have a try with invoking method in AppDelegate.cs
public override void OnActivated(UIApplication uiApplication)
{
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
// If VS has updated to the latest version , you can use StatusBarManager , else use the first line code
// UIView statusBar = new UIView(UIApplication.SharedApplication.StatusBarFrame);
UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
statusBar.BackgroundColor = UIColor.Red;
UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
}
else
{
UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
{
statusBar.BackgroundColor = UIColor.Red;
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.BlackOpaque;
}
}
base.OnActivated(uiApplication);
}
Note :
Not sure this solurion will always work in AppDelegate.cs, better invoked in Controller.cs's method , because from iOS 13 , Apple have modified the architure of AppDelegate and added SceneDelegate to project.
This also works, if used in ViewDidAppear override in a UIViewController. I previously tested in ViewWillAppear and even that was too soon to have a KeyWindow non-null:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if (UIDevice.CurrentDevice.CheckSystemVersion(13, 0))
{
//Obj-C:
// UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame] ;
// statusBar.backgroundColor = [UIColor redColor];
// [[UIApplication sharedApplication].keyWindow addSubview:statusBar];
// Xamarin.iOS:
UIView statusBar = new UIView(UIApplication.SharedApplication.KeyWindow.WindowScene.StatusBarManager.StatusBarFrame);
statusBar.BackgroundColor = UIColor.Red;
UIApplication.SharedApplication.KeyWindow.AddSubview(statusBar);
}
else
{
UIView statusBar = UIApplication.SharedApplication.ValueForKey(new NSString("statusBar")) as UIView;
if (statusBar.RespondsToSelector(new ObjCRuntime.Selector("setBackgroundColor:")))
{
statusBar.BackgroundColor = UIColor.Red;
statusBar.TintColor = UIColor.White;
UIApplication.SharedApplication.StatusBarStyle = UIStatusBarStyle.BlackOpaque;
}
}
}
And that is the same solution provided in the duplicate question: https://stackoverflow.com/a/58028658/2913599
Could you please try the below solution. it works fine for me in the same scenario
if (#available(iOS 13.0, *)) {
UIView *statusBar = [[UIView alloc]initWithFrame:[UIApplication sharedApplication].keyWindow.windowScene.statusBarManager.statusBarFrame];
statusBar.backgroundColor = [UIColor redColor];
[[UIApplication sharedApplication].keyWindow addSubview:statusBar];
} else {
// Fallback on earlier versions
UIView *statusBar=[[UIApplication sharedApplication] valueForKey:#"statusBar"];
statusBar.backgroundColor = [UIColor redColor];
[statusBar setNeedsDisplay];
}

How to detect Xamarin Forms tabbed page click - iOS?

My scenario is, I have a Tabbed page in Xamarin Forms:
public partial class MainPage : TabbedPage
{
public MainPage()
{
InitializeComponent();
var playPage = new NavigationPage(new PlayPage())
{
Title = "Play",
Icon = "play.png"
};
var settingsPage = new NavigationPage(new SettingsPage())
{
Title = "Settings",
Icon = "settings.png"
};
var favoritesPage = new NavigationPage(new FavoritesPage())
{
Title = "Favorites",
Icon = "fave.png"
};
var aboutPage = new NavigationPage(new AboutPage())
{
Title = "About",
Icon = "info.png"
};
Children.Add(playPage);
Children.Add(favoritesPage);
Children.Add(settingsPage);
Children.Add(aboutPage);
}
}
I want to add a pause and play function to my app. On start up, the PlayPage would initially have the play.png icon and when I click on the PlayPage again it would change the icon to pause.png. Page is not changing just the page icon. Anyone has any idea how this could be done?
Edit:
So I have created a custom renderer, in OnElementChanged I utilize the ViewControllerSelected:
var tabbarController = (UITabBarController)this.ViewController;
if (null != tabbarController)
{
tabbarController.ViewControllerSelected += OnTabBarReselected;
}
And my OnTabBarReselected I have:
private void OnTabBarReselected(object sender, UITabBarSelectionEventArgs e)
{
switch (TabBar.SelectedItem.Title)
{
case "Play":
TabBar.SelectedItem.Title = "Pause";
TabBar.SelectedItem.Image = UIImage.FromFile("pause.png");
break;
}
}
This only does half of the work. It changes the Title of the selected tab bar from Play to Pause after I click on the same tab but not the Icon. The icon remains "play.png" until I get out of that tab page (selecting another tab). Anyone has any idea why?
You will need to implement a custom renderer to pull this off. There are some implementations on James Montemagno's blog where he talks about changing the icons.
iOS:
http://motzcod.es/post/138225183932/tintcolor-selectedimage-xamarin-forms-ios
Droid:
http://motzcod.es/post/157544468267/xamarin-forms-android-selected-and-unselected-tab-colors
This is however not necessarily related to your requirement of tapping the icon and changing that specific icon since all this code only runs when the page initially loads. It could be a nice starting point though. Check in there if there's a property on TabbedPage that changes when you tap on the current tab and change the icon at that point.
You also have a OnCurrentPageChanged event you can override in TabbedPage, but that isn't called when the page is already active.

Binding a HTML control into a stacklayout using xamarin forms

Being a newbie to Xamrin I am struggling to adding some HTML to a StackLayout via Xamarin Forms. I have tried quite a few things and had a Google around.
Firstly I can't work out which bindable object I am supposed to be using. As I cannot find a straight answer on Google/Xamarin I am going to assume this is not as easy I was hoping.
var nameEntry = new Label ();
nameEntry.SetBinding (Label.TextProperty, "Club.ClubName");
var webView = new WebView ();
webView.SetBinding ( ??? , "Club.Description");
var content = new StackLayout {
Children = {
nameEntry,
???
}
};
I am not sure if this is possible within Xamarin forms itself. Can anyone help?
I should point out my data for the form is being retrieved asynchronously on a remote json endpoint
protected override void OnAppearing ()
{
base.OnAppearing ();
if (ViewModel == null || ViewModel.IsLoading)
return;
ViewModel.LoadItemsCommand.Execute (Club.ClubId);
}
My remote json api contains, Description contatins a HTML snippet which I would like to use.
{
ClubName: "Stourbridge",
Description: "<p>This club meets every TWO weeks on a <b>Friday</b>.</p>"
...
}
Try the following example that will show how to do the bindings.
Note that you have to use a HtmlWebViewSource to achieve this, and bind the WebView.Source to this.
Clicking the button will change the view model and update the WebView appropriately to the newly changed text.
StackLayout objStackLayout = new StackLayout();
MyView objMyView = new MyView();
objMyView.MyHtml = "<html><head></head><body><h1>Title</h1><p>Some body text</p></body></html>";
HtmlWebViewSource objHtmlWebViewSource = new HtmlWebViewSource();
objHtmlWebViewSource.SetBinding(HtmlWebViewSource.HtmlProperty, "MyHtml");
objHtmlWebViewSource.BindingContext = objMyView;
WebView objWebview = new WebView();
objWebview.HorizontalOptions = LayoutOptions.FillAndExpand;
objWebview.VerticalOptions = LayoutOptions.FillAndExpand;
objWebview.Source = objHtmlWebViewSource;
Button objMyButton2 = new Button();
objMyButton2.Text="Change Html";
objMyButton2.Clicked+=((o2,e2)=>
{
objMyView.MyHtml = "<html><head></head><body><h1>Title</h1><p>Some body text that has changed.</p></body></html>";
});
objStackLayout.Children.Add(objMyButton2);
objStackLayout.Children.Add(objWebview);
The view model is just a simple one with a bindable property as below:-
public class MyView
: Xamarin.Forms.View
{
public static readonly BindableProperty MyHtmlProperty = BindableProperty.Create<MyView, string>(p => p.MyHtml, default(string));
public string MyHtml
{
get { return (string)GetValue(MyHtmlProperty); }
set { SetValue(MyHtmlProperty, value); }
}
}
Before clicking the button gives:-
After clicking the button, adjusts the view model, and automatically updates the control via the binding giving:-

visual studio xamarin forms mvvm

I'm starting a new project, and id like to use MVVM - I really like this pattern, and I have been using it in all my windows phone 8.1 apps. But moving to xamarin is a jungle! I usually use mvvm light, and I have a nice basic implementation I use every time a create a new project - but I can't find a really good sample that shows exactly what I need.
What I want to do is make a xamarin shared (or portable) project, that shares the views across all platforms. I want to write create the view using code-behind - so no xaml.
Does anyone have experience with this and can point me to a good sample?
I'm also wondering if I need to use a thirtyparty framework afterall, since navigating seems pretty easy.
There are many samples on to be found. My favorite site for Xamarin.Forms samples is Xamarin Forms in Anger.
Let's take a look at the Jobbberr sample:
using System;
using Xamarin.Forms;
namespace InAnger.Jobbberr
{
public class SettingsPage : ContentPage
{
public SettingsPage ()
{
Style = AppStyle.SettingsPageStyle;
var pageTitle = new Frame () {
Style = AppStyle.PageTitleLabelFrameStyle,
Padding = new Thickness(0,Device.OnPlatform(15,0,0),0,10),
Content = new Label {
Style = AppStyle.PageTitleLabelStyle,
Text = "Settings",
}
};
var signoutButton = new Button () {
VerticalOptions = LayoutOptions.EndAndExpand,
HorizontalOptions = LayoutOptions.Center,
Text = "Sign Out",
TextColor = AppStyle.DarkLabelColor,
};
Content = new StackLayout {
VerticalOptions = LayoutOptions.FillAndExpand,
Padding = new Thickness (20),
Children = {
pageTitle,
new BoxView() {
HeightRequest = 1,
BackgroundColor = AppStyle.DarkLabelColor,
},
new SettingsUserView(),
new SyncView (),
new SettingsSwitchView ("GPS"),
new SettingsSwitchView ("Jobs Alert"),
signoutButton,
new StatusBarView()
}
};
}
}
}
What do you see here?
The new class SettingsPage derives from ContentPage. The controls pageTitle and signoutButton are created in its constructor. In the end you see how a StackLayout is being created, filled with the controls and set as content of the page. That's how to create a Page in code.
How to apply MVVM?
Set BindingContext = ViewModel in the first row of the constructor (create a new view model or locate it by via a ViewModelLocator or anything).
Let's say for example you want to bind the Text and Command property of signoutButton to the view model's properties SignOutButtonText and SignoutCommand. You would change the creation of the button to this:
var signoutButton = new Button () {
VerticalOptions = LayoutOptions.EndAndExpand,
HorizontalOptions = LayoutOptions.Center,
TextColor = AppStyle.DarkLabelColor,
};
signoutButton.SetBinding(Button.TextProperty, "SignOutButtonText");
signoutButton.SetBinding(Button.CommandProperty, "SignoutCommand");

Categories

Resources