I need a way to style Monotouch Dialogs RootElement. I need to change the background and font color.
I'm have created a custom RootElement as below
public class ActivityRootElement : RootElement
{
public ActivityRootElement (string caption) : base (caption)
{
}
public ActivityRootElement(string caption, Func<RootElement, UIViewController> createOnSelected) : base (caption, createOnSelected)
{
}
public ActivityRootElement(string caption, int section, int element) : base (caption, section, element)
{
}
public ActivityRootElement(string caption, Group group) : base (caption, group)
{
}
public override UITableViewCell GetCell (UITableView tv)
{
tv.BackgroundColor = Settings.RootBackgroundColour;
return base.GetCell (tv);
}
protected override void PrepareDialogViewController(UIViewController dvc)
{
dvc.View.BackgroundColor = Settings.RootBackgroundColour;
base.PrepareDialogViewController(dvc);
}
}
I am then calling the custom root element as below passing in a custom DialogController
section.Add (new ActivityRootElement(activity.Name, (RootElement e) => {
return new ActivityHistoryDialogViewController (e,true);
}));
The root Element style is not been applied. Any help would be apprciated!!
If you want the color to be the only thing you see in the TableViewController, you need to set the BackgrounView to null. There is a view overtop to apply the styling, which will hide the color you are looking for.
Try this:
public override UITableViewCell GetCell (UITableView tv)
{
tv.BackgroundColor = Settings.RootBackgroundColour;
tv.BackgroundView = null;
return base.GetCell (tv);
}
protected override void PrepareDialogViewController(UIViewController dvc)
{
dvc.TableView.BackgroundColor = Settings.RootBackgroundColour;
dvc.TableView.BackgroundView = null;
base.PrepareDialogViewController(dvc);
}
In order to get this to work, I had to override the MakeViewController method and cast the UIViewController that it normally returns to a UITableViewController, then make my edits.
protected override UIViewController MakeViewController()
{
var vc = (UITableViewController) base.MakeViewController();
vc.TableView.BackgroundView = null;
vc.View.BackgroundColor = UIColor.Red; //or whatever color you like
return vc;
}
Related
I'm using
SKStoreReviewController.RequestReview ();
in a dependency service for Xamarin.Forms. However this is now deprecated from iOS 14. And I want to know how to integrate with the new
UIWindowScene
requestReview(in windowScene: UIWindowScene)
in Xamarin.Forms
First, you can try to add SKStoreReviewController.RequestReview(Window.WindowScene) in the FinishedLaunching method of your xxx.ios->AppDelegate.cs; start the project to see if it is correct.
If the above method goes wrong, using DependencyService is the right way to go.
Here is the interface code:
public interface MyInterface
{
void RequestReview();
}
Here is the implementation method of the interface in ios:
[assembly: Dependency(typeof(MyInterfaceImpl))]
namespace App19.iOS
{
public class MyInterfaceImpl : MyInterface
{
public void RequestReview()
{
var myv = UIDevice.CurrentDevice.CheckSystemVersion(14, 0);
if (myv)
{
UIWindow window = UIApplication.SharedApplication.Delegate.GetWindow();
SKStoreReviewController.RequestReview(window.WindowScene);
}
else
{
SKStoreReviewController.RequestReview();
}
}
}
}
You can call it in the OnStart method inside APP.xaml.cs:
protected override void OnStart()
{
if (Device.RuntimePlatform == Device.iOS)
{
DependencyService.Get<IReviewService>().RequestReview();
}
}
I might have a solution:
using StoreKit;
using UIKit;
public void RequestReview(){
var scene = UIApplication.SharedApplication.KeyWindow.WindowScene;
if (scene is null)
{
//handle the scene being null here
return;
}
SKStoreReviewController.RequestReview(scene);
}
I've found multiple ways of getting the current scene and they both worked when I tested them:
var scene = UIApplication.SharedApplication.KeyWindow.WindowScene;
var alsoScene = UIApplication.SharedApplication.Delegate.GetWindow()?.WindowScene;
You can read more about the difference between the two in this issue
The complete solution where you check the iOS version in order to call the right thing might look like this
using StoreKit;
using UIKit;
public void RequestReview()
{
var isIos14OrAbove = UIDevice.CurrentDevice.CheckSystemVersion(14, 0);
if (isIos14OrAbove)
{
var scene = UIApplication.SharedApplication.KeyWindow.WindowScene;
if (scene is null)
{
//handle the scene being null here
return;
}
SKStoreReviewController.RequestReview(scene);
}
else
{
SKStoreReviewController.RequestReview();
}
}
Hope this helps.
I'm coding in Xamarin.iOs and I'd like to add a border for bottom of the UITextField.
So it's simple, I've googled it and (I personalized it to Xamarin.ios and) I have this code from this link.
UITextField _textField = new UITextField();
_textField.Placeholder = placeHolder;
_textField.AutocapitalizationType = autocap.Value;
_textField.AutocorrectionType = uitextAutocorrect.Value;
_textField.BorderStyle = borderstyle.Value;
_textField.SecureTextEntry = secureTextEntry.Value;
_textField.KeyboardType = uiKeyboardType.Value;
return _textField;
As you know I cannot put this part of code in constructor of my ViewController and also in ViewWillAppear. So I have to put it in ViewDidAppear, BUT it's too late, it means that when I run ViewController, it show the textField without borders, and just after a few milliseconds, the border will appear which is toooo late for me.
Any idea for this display problem?
EDIT:
So I ask my question in much more detail :
here is my complete code:
public class ChangePasswordViewController: UIViewController
{
//private MainViewModel _viewModel;
private UILabel _oldPasswordLabel;
private UITextField _oldPasswordTextField;
private UILabel _newPasswordLabel;
private UITextField _newPasswordTextField;
private UILabel _confirmPasswordLabel;
private UITextField _confirmPasswordTextField;
private User currentUser;
private UIButton _saveNewPassworBtn;
public ChangePasswordViewController(User user)
{
currentUser = user;
}
private void _saveNewPassworBtn_TouchUpInside(object sender, EventArgs e)
{
//do sth
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
// Perform any additional setup after loading the view, typically from a nib.
//_viewModel = new MainViewModel();
Title = Texts.ChangePassword;
View.BackgroundColor = UIColor.White;
_oldPasswordLabel = InitUILabel(Texts.OldPasswordTxt, alignment: UITextAlignment.Right);
_oldPasswordLabel.Font = UIFont.BoldSystemFontOfSize(14);
_oldPasswordLabel.AdjustsFontSizeToFitWidth = true;
_oldPasswordTextField = InitUITextField(Texts.OldPassword, secureTextEntry: true);
this._oldPasswordTextField.ShouldReturn += (textField) => {
_newPasswordTextField.BecomeFirstResponder();
return true;
};
_oldPasswordTextField.BorderStyle = UITextBorderStyle.None;
_newPasswordLabel = InitUILabel(Texts.NewPasswordTxt, alignment: UITextAlignment.Right);
_newPasswordLabel.Font = UIFont.BoldSystemFontOfSize(14);
_newPasswordTextField = InitUITextField(Texts.NewPassword, secureTextEntry: true);
this._newPasswordTextField.ShouldReturn += (textField) => {
_confirmPasswordTextField.BecomeFirstResponder();
return true;
};
_newPasswordTextField.BorderStyle = UITextBorderStyle.None;
_confirmPasswordLabel = InitUILabel(Texts.ConfirmPasswordTxt, alignment: UITextAlignment.Right);
_confirmPasswordLabel.Font = UIFont.BoldSystemFontOfSize(14);
_confirmPasswordTextField = InitUITextField(Texts.NewPassword, secureTextEntry: true);
this._confirmPasswordTextField.ShouldReturn += (textField) => {
textField.ResignFirstResponder();
return true;
};
_confirmPasswordTextField.BorderStyle = UITextBorderStyle.None;
_saveNewPassworBtn = InitUIButton(Texts.Save, _saveNewPassworBtn_TouchUpInside, Colors.MainColor, UIColor.White);
View.AddSubviews(_oldPasswordLabel, _oldPasswordTextField, _newPasswordLabel, _newPasswordTextField,
_saveNewPassworBtn, _confirmPasswordLabel, _confirmPasswordTextField);
//
//constraints
var hMargin = 10;
var hMiniMargin = 5;
var vMargin = 10;
View.SubviewsDoNotTranslateAutoresizingMaskIntoConstraints();
View.AddConstraints(
_oldPasswordLabel.AtTopOf(View, 100),
_oldPasswordLabel.AtLeftOf(View, hMargin),
_oldPasswordTextField.Below(_oldPasswordLabel, hMiniMargin),
_oldPasswordTextField.AtLeftOf(View, hMargin),
_oldPasswordTextField.WithSameWidth(View).Minus(hMargin * 2),
_newPasswordLabel.Below(_oldPasswordTextField, vMargin),
_newPasswordLabel.AtLeftOf(View, hMargin),
_newPasswordTextField.Below(_newPasswordLabel, hMiniMargin),
_newPasswordTextField.AtLeftOf(View, hMargin),
_newPasswordTextField.WithSameWidth(View).Minus(hMargin * 2),
_confirmPasswordLabel.Below(_newPasswordTextField, vMargin),
_confirmPasswordLabel.AtLeftOf(View, hMargin),
_confirmPasswordTextField.Below(_confirmPasswordLabel, hMiniMargin),
_confirmPasswordTextField.AtLeftOf(View, hMargin),
_confirmPasswordTextField.WithSameWidth(View).Minus(hMargin * 2),
_saveNewPassworBtn.Below(_confirmPasswordTextField, vMargin * 3),
_saveNewPassworBtn.WithSameWidth(View).Minus(20),
_saveNewPassworBtn.AtLeftOf(View, 10)
);
_newPasswordTextField = SetBottomBorderLine(_newPasswordTextField);
_confirmPasswordTextField = SetBottomBorderLine(_confirmPasswordTextField);
_oldPasswordTextField = SetBottomBorderLine(_oldPasswordTextField);
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
this.NavigationItem.SetLeftBarButtonItem(new UIBarButtonItem(Texts.Cancel,UIBarButtonItemStyle.Plain, (sender, args) =>
{
this.NavigationController.SetNavigationBarHidden(false, false);
this.NavigationController.PushViewController(new ProfileViewController(), false);
}), true);
}
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
}
}
In these lines:
_newPasswordTextField = SetBottomBorderLine(_newPasswordTextField);
_confirmPasswordTextField = SetBottomBorderLine(_confirmPasswordTextField);
_oldPasswordTextField = SetBottomBorderLine(_oldPasswordTextField);
I put the border for my textfields BUT! If I put these line in ViewDidLoad or ViewWillAppear or ViewDidLayoutSubviews, it doesn't show the changes and if I put them in ViewDidAppear, it's shows perfectly with some milliseconds delay (after showing the content of my page!!!). Do you have any solution for me?
It caused by using Cirrious.FluentLayouts.
When we set the Constraints in ViewDidLoad, those control will not finish rendering before ViewDidAppear .
Print the frame of those textfields in ViewDidLoad, ViewWillAppear ,ViewDidAppear , you can find that only in ViewDidAppear the frame is not equal (0,0,0,0).
To solve the problem, you can add View.LayoutIfNeeded in ViewDidLoad to force update the view.
View.AddConstraints(
//xxx
);
View.LayoutIfNeeded(); //Add this
_newPasswordTextField = SetBottomBorderLine(_newPasswordTextField);
_confirmPasswordTextField = SetBottomBorderLine(_confirmPasswordTextField);
_oldPasswordTextField = SetBottomBorderLine(_oldPasswordTextField);
I want to hide the title bar when i scrolling the items in the ListView and i want to show the title bar after scrolling. Suggest any ideas to solve this issue.
First add the Xml View into ActionBar like this:
LayoutInflater inflater = (LayoutInflater) getActionBar()
.getThemedContext().getSystemService(LAYOUT_INFLATER_SERVICE);
View customActionBarView = inflater.inflate(R.layout.main, null);
final ActionBar actionBar = getActionBar();
actionBar.setDisplayOptions(
ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME);
actionBar.setCustomView(customActionBarView,
new ActionBar.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
setContentView(R.layout.main);
Then do the changes in onScrollStateChanged() method:
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case SCROLL_STATE_IDLE:
actionBar.show();
break;
case SCROLL_STATE_TOUCH_SCROLL:
actionBar.hide();
break;
}
}
//declare this two globally
public static int ch = 0, cht = 1;
int myLastVisiblePos;
//Then add onScrollListener to your ListView
list.setOnScrollListener(new OnScrollListener() {
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
int currentFirstVisPos = view.getFirstVisiblePosition();
if (currentFirstVisPos > myLastVisiblePos) {
if (ch == 1) {
ch++;
cht = 1;
getActionBar().hide();
} else if (ch == 0) {
getActionBar().show();
ch++;
}
}
if (currentFirstVisPos < myLastVisiblePos)
if (cht == 1)
getActionBar().show();
myLastVisiblePos = currentFirstVisPos;
}
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
// TODO Auto-generated method stub
}
});
This solution worked for me very good:
// mLastFirstVisibleItem defined globally
quranTextList.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
/**
* Hide actionbar when scroll down
*/
if (mLastFirstVisibleItem < firstVisibleItem)
if (getSupportActionBar().isShowing())
getSupportActionBar().hide();
if (mLastFirstVisibleItem > firstVisibleItem)
if (!getSupportActionBar().isShowing())
getSupportActionBar().show();
mLastFirstVisibleItem = firstVisibleItem;
}
});
Source: Android ActionBar hide/show when scrolling list view
I've been reading about ViewHolder Pattern and it's effects on ListView scrolling performance lately.
For a smooth scrolling, fast ListView should i avoid using OnClickListener registerations inside adapter getView() method such as:
#Override
public View getView(final int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if (convertView == null)
{
holder = new ViewHolder();
LayoutInflater vi = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(mResourceId, null);
holder.btn1 = (TextView) convertView.findViewById(R.id.btn1);
holder.img1 = (TextView) convertView.findViewById(R.id.img1);
convertView.setTag(holder);
} else { holder = (ViewHolder) convertView.getTag(); }
final Items item = getItem(position);
holder.btn1.setText(item.btnText);
holder.img1.setBackgroundResource(item.imgSource);
holder.img1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) { /* .. my code block USING POSITION ARG .. */ }
}
holder.btn1.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) { /* .. My Code Block USING POSITION ARG .. */ }
}
return convertView;
}
If so, is registering a OnItemClickListener to ListView instance as following sample does a good practice:
myListView.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View view, int position, long id) {
if (view.getId() == R.id.btn1)
{
// my code block USING POSITION ARG
}
else if (view.getId() == R.id.img1)
{
// my code block USING POSITION ARG
}
}
});
It's better to use a Wrapper to access to your View and to define your OnClickListener earlier (and outside the adapter for a better usability).
The following sample show how to handle 2 clickable View on one single item of the ListView with good performance:
public class ItemAdapter extends BaseAdapter {
private List<Item> items;
private ItemWrapper wrapper = null;
private OnClickListener onMyItemClickListener1;
private OnClickListener onMyItemClickListener2;
public ItemAdapter(Context context, List<Item> items, OnClickListener onMyItemClickListener1, OnClickListener onMyItemClickListener2) {
this.inflater = LayoutInflater.from(context);
this.items = items;
this.onMyItemClickListener1 = onMyItemClickListener1;
this.onMyItemClickListener2 = onMyItemClickListener2;
}
#Override
public int getCount() {
return items.size();
}
#Override
public Object getItem(int position) {
return items.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public synchronized View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
row = inflater.inflate( R.layout.item, null);
wrapper = new ItemWrapper(row);
row.setTag(wrapper);
} else {
wrapper = (ItemWrapper)row.getTag();
}
Item item = getItem(position);
wrapper.getClickView1().setOnClickListener(onMyItemClickListener1);
wrapper.getClickView2().setOnClickListener(onMyItemClickListener2);
return(row);
}
}
public class ItemWrapper {
private View baseView;
private View clickView1;
private View clickView2;
public ItemWrapper(View baseView) {
this.baseView = baseView;
}
public View getClickView1() {
if ( clickView1 == null) {
clickView1 = (View)baseView.findViewById(R.id.clickView1);
}
return(clickView1);
}
public View getClickView2() {
if ( clickView2 == null) {
clickView2 = (View)baseView.findViewById(R.id.clickView2);
}
return(clickView2);
}
}
Using a UIWebView in my application and I cant seem to release the memory it's using.
I have a UIButton which loads a view controller which holds the UIWebView.
When the webView is loaded the Real Memory (checking it using Instruments) rises but doesn't get lower after my attempts to release it.
When the button is pressed:
if (childBroswer != null)
{
childBroswer.Dispose();
childBroswer = null;
}
childBroswer = new ChildBrowser(url);
AppDelegate d = (AppDelegate)UIApplication.SharedApplication.Delegate;
if ( d.rootNavigationController.RespondsToSelector(
new MonoTouch.ObjCRuntime.Selector("presentViewController:animated:completion:")))
{
d.rootNavigationController.PresentViewController(childBroswer, true, null);
}
else
{
d.rootNavigationController.PresentModalViewController(childBroswer, true);
}
Child browser class:
public partial class ChildBrowser : UIViewController
{
public string _url {get;set;}
UIActivityIndicatorView activityIndicator;
UIWebView webBrowser;
NSUrl nsURL;
NSUrlRequest nsURLRequest;
public ChildBrowser (string url) : base ("ChildBrowser", null)
{
nsURL = new NSUrl(url);
nsURLRequest = new NSUrlRequest(nsURL);
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
if (webBrowser == null)
{
webBrowser = new UIWebView(new RectangleF(0,41,320,380));
}
this.Add(webBrowser);
webBrowser.LoadFinished += (object sender, EventArgs e) => {
activityIndicator.StopAnimating();
} ;
webBrowser.LoadStarted += (object sender, EventArgs e) => {
if (activityIndicator == null)
{
activityIndicator = new UIActivityIndicatorView(new RectangleF(UIScreen.MainScreen.Bounds.Width / 2 - 50,
UIScreen.MainScreen.Bounds.Height / 2 - 30,100,100));
activityIndicator.Color = UIColor.Black;
}
activityIndicator.StartAnimating();
this.Add(activityIndicator);
} ;
webBrowser.LoadHtmlString("<html><head></head><body></body></html>",null); //stringByEvaluatingJavaScriptFromString:#"document.body.innerHTML = \"\";"];
webBrowser.LoadRequest(nsURLRequest);
closeBrowser.TouchUpInside += handleClose;
// Perform any additional setup after loading the view, typically from a nib.
}
private void handleClose(object sender, EventArgs e)
{
webBrowser.Delegate = null;
webBrowser.StopLoading();
webBrowser.Dispose();
DismissViewController(true,delegate {
closeBrowser.TouchUpInside -= handleClose;
webBrowser.RemoveFromSuperview();
this.Dispose();
} );
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
}
public override void ViewDidDisappear(bool animated)
{
base.ViewDidDisappear(true);
//webBrowser.RemoveFromSuperview();
}
public void webViewDidFinishLoad(UIWebView webView)
{
//string u = "";
}
protected override void Dispose(bool disposing)
{
ReleaseDesignerOutlets();
base.Dispose(disposing);
}
What am i missing?
Thank you!
Memory for native objects is not reclaimed when you call Dispose. Calling Dispose only drops the managed reference - but the ObjC runtime (reference counted) won't free the object until there's no native reference as well. See this Q&A for more details...
This means that code such as:
this.Add(webBrowser);
will create such a native reference and is likely (i.e. if not removed elsewhere) to keep the webBrowser instance alive as long as this is alive. If you keep Adding then you memory usage will keep growing.
I'm the OP - It seems that releasing the memory usage of UIWebView is quite hard, even when doing so in Objective-C.
What I have eventually done, which didn't solve the problem but improved the memory release is adding the following lines when closing the UIWebView:
NSUrlCache.SharedCache.RemoveAllCachedResponses();
NSUrlCache.SharedCache.DiskCapacity = 0;
NSUrlCache.SharedCache.MemoryCapacity = 0;
webView.LoadHtmlString("",null);
webView.EvaluateJavascript("var body=document.getElementsByTagName('body')[0];body.style.backgroundColor=(body.style.backgroundColor=='')?'white':'';");
webView.EvaluateJavascript("document.open();document.close()");
webView.StopLoading();
webView.Delegate = null;
webView.RemoveFromSuperview();
webView.Dispose();