I got a UIViewController that init a UIView.
This view containing Interactive Elements like UITextField or UIButton.
View is added on ViewDidLoad, at the bottom of the method to be sure that when I made it visible it can be reach by user interaction.
But when I show the view, no interaction can be done on this View.
Is this only possible? Am I doing something wrong?
The View
public class AddBusinessEventView : UIView
{
public UILabel LblTitle;
public UITextField TxtType;
public UIButton BtnClose;
public AddBusinessEventView(nfloat bw, nfloat bh)
{
//Bouton pour fermer le popup
BtnClose = new UIButton();
BtnClose.SetImage(UIImage.FromFile("Images/Boutons/lightbox_close.png"), UIControlState.Normal);
BtnClose.Frame = new CGRect(bw - 80, 30, BtnFermer.ImageView.Image.CGImage.Width * 0.5, BtnFermer.ImageView.Image.CGImage.Height * 0.5);
//Doit se trouver par dessus txtSite et ajouté après dans la vue pour se trouvé en premier plan
LblTitle = new UILabel();
LblTitle.Frame = new CGRect((bw - (lw + 200)) / 2, 100, lw + 200, 30);
LblTitle.Text = "Fill with your event elements";
LblTitle.Font = UIFont.FromName("GillSans-Bold", 22);
LblTitle.TextColor = UIColor.FromRGB(211, 3, 67);
LblTitle.TextAlignment = UITextAlignment.Center;
TxtType = new UITextField(new CGRect((bw - 750) / 2, 140, 350, 40));
TxtType.BackgroundColor = UIColor.White;
TxtType.TextAlignment = UITextAlignment.Center;
TxtType.BorderStyle = UITextBorderStyle.RoundedRect;
TxtType.AutocorrectionType = UITextAutocorrectionType.No;
TxtType.AutocapitalizationType = UITextAutocapitalizationType.AllCharacters;
TxtType.Placeholder = "Type";
AddSubviews(BtnClose, LblTitle, TxtType);
}
}
The UIViewController
partial class EvenementViewController : EnhancedUIViewController
{
AddBusinessEventView AddBusinessEventView;
public EvenementViewController(IntPtr handle) : base(handle) { }
public EvenementViewController() : base() { }
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
if (myEvent == null)
{
ShowAddBusinessEventView();
}
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
nfloat bw = View.Bounds.Width;
nfloat bh = View.Bounds.Height;
//Another Elements are adding to view here
//...
AddBusinessEventView = new AddBusinessEventView(bw, bh);
AddBusinessEventView.Hidden = true;
//Much more View.Add with all elements here
//...
View.Add(AddBusinessEventView);
AddBusinessEventView.BtnType.TouchUpInside += BtnClose_TouchUpInside;
}
#region BusinessEventAdd
void ShowAddBusinessEventView()
{
UIView.Animate(duration: 1,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut,
animation: () =>
{
AddBusinessEventView.Alpha = 1.0f;
},
completion: () =>
{
AddBusinessEventView.Hidden = false;
AddBusinessEventListener();
}
);
}
void HideAddBusinessEventView()
{
UIView.Animate(duration: 1,
delay: 0,
options: UIViewAnimationOptions.CurveEaseInOut,
animation: () =>
{
AddBusinessEventView.Alpha = 0.0f;
},
completion: () =>
{
AddBusinessEventView.Hidden = true;
RemoveBusinessEventListener();
}
);
}
void BtnClose_TouchUpInside(object sender, EventArgs e)
{
System.Diagnostics.Debug.Print("Touching myself");
}
#endregion
}
please concidere EnhancedViewController as standard UIViewController, I'm juste adding some enhancements to show users a message from an Overlay.
As I said, we can't interact neither with TxtType nor BtnClose.
Edit :
Don't sure if it can help; but when the View is added to the Main View on the UIViewController, it display well, but all user interaction are catches on element under this View
i.e : The AddBusinessEventView act as a popup, so it covered all other element, when i press an element, if another element adding prior of this View is under, it's this element rather than the AddBusinessEventView element that catch the touch event.
As said, the main purpose is to cut View element on different file for more readability and maintenability of this application.enter code here
There is no reason you can't do that.
You can architect your code like
UIViewController
Controller1
Controller2
...
Views
View1
View2
...
And then use View1 and View2 in any of Controller.
I tried your code and I got all the things popup correctly.
As you said, if view show up on screen but it's element from another View from below that interact with user maybe you can try to bring your View to front.
Try this
View.BringSubviewToFront(AddBusinessEventView);
At the end of your ViewDidLoad function.
Related
I have an application that from the main app "hamburger" menu, if an option is selected I want to show a PickerView for the user to select a number from.
Because it's needs to be accessible throughout the app from this menu I built the UIPickerView in the AppDelegate.cs (as that's where the UINavigationController code is and from that the menu).
Everything shows up correctly: User selected menu button -> menu displays -> User selects "Show Picker" button -> Picker displays with all items. But once the picker displays, you can't scroll the options, nor does the "Done" button I've added register clicks. In fact text fields from the ViewController behind this popup can be clicked on through the popup.
I'm not sure why this UIPickerView is non interactive, does anyone have some thoughts?
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
// create a new window instance based on the screen size
window = new UIWindow(UIScreen.MainScreen.Bounds);
// instantiate the navigation controller
nav = new UINavigationController(new SplashController());
vwNav = new UIView(nav.NavigationBar.Bounds);
var pickerView = new UIPickerView(new CGRect(275f, (UIScreen.MainScreen.Bounds.Size.Height / 2) - (375f / 2), 275f, 375f));
pickerView.ShowSelectionIndicator = true;
var myPickerViewModel = new PickerViewModel(itemList);
pickerView.Model = myPickerViewModel;
myPickerViewModel.PickerChanged += (sender, e) => {
var temp = myPickerViewModel.SelectedItem;
};
// Set up toolbar
var toolbar = new UIToolbar();
toolbar.SizeToFit();
toolbar.Hidden = false;
UILabel titleLabel = new UILabel();
titleLabel.Text = "Select an Item";
titleLabel.Frame = new RectangleF(75, 13, 200, 20);
UIButton doneButton = new UIButton(UIButtonType.Custom);
doneButton.Frame = new RectangleF(40, 335, (float)200, 30);
doneButton.SetTitle("Done", UIControlState.Normal);
doneButton.TouchDown += (sender, e) =>
{
pickerView.Hidden = true;
};
toolbar.AddSubview(titleLabel);
toolbar.AddSubview(doneButton);
pickerView.AddSubview(toolbar);
pickerView.Hidden = true;
btnMenu = new UIButton(UIButtonType.Custom);
btnMenu.Frame = new CGRect(vwNav.Frame.Right - 45, 0, 45, nav.NavigationBar.Bounds.Height);
btnMenu.SetImage(imgMenu, UIControlState.Normal);
btnMenu.SetImage(imgMenu, UIControlState.Selected);
btnMenu.TouchUpInside += (object sender, EventArgs e) =>
{
UIAlertView alert = new UIAlertView();
alert.Title = "Settings";
alert.AddButton("Open PickerView");
alert.AddButton("Exit");
alert.Dismissed += delegate (object alertSender, UIButtonEventArgs args)
{
if (args.ButtonIndex == 0)
{
pickerView.Hidden = false;
}
else
return;
}
}
vwNav.AddSubviews(btnMenu, pickerView);
nav.NavigationBar.Layer.BorderWidth = 2f;
nav.NavigationBar.Layer.BorderColor = (UIColor.FromPatternImage(imgNavBar)).CGColor;
nav.NavigationBar.AddSubviews(vwNav);
// If you have defined a root view controller, set it here:
this.window.RootViewController = nav;
this.window.MakeKeyAndVisible();
return true;
}
I removed some of the unrelated code (a few nav.PushViewController() for different screens and other menu options) to keep it as clear as I could.
From your code, the frame of vwNav is nav.NavigationBar.Bounds, and the frame of pickerView is new CGRect(275f, (UIScreen.MainScreen.Bounds.Size.Height / 2) - (375f / 2), 275f, 375f), if you add pickView to vwNav, the pickerView is out of bounds of vwNav.
Remember that views don't receive touch events where they're outside the bounds of their superview.
That's the cause of your issue.
Solution:
I don't think you should built the UIPickerView in the AppDelegate.cs, create it in your ViewController instead.
You can also add buttons to Navigationbar in ViewController :
UIBarButtonItem btn = new UIBarButtonItem();
btn.Image = UIImage.FromFile("Image");
btn.Clicked += (sender, e) => { System.Diagnostics.Debug.WriteLine("show picker"); };
NavigationItem.RightBarButtonItem = btn;
And remember to add picker to the View of Viewcontroller.
Refer: uinavigationitem and add-uibarbuttonitem-to-navigation-bar-in-xamarin-ios
Feel free to ask me any question:).
I have a UITextView and a Button.
I need that the keyboard stays opened when the user taps the button.
I tried to use the ShouldEndEditing function to return False, but then the user can never close the keyboard again.
Any ideas?
I'm using Xamrin Forms.
In Objective-C you would set a Done Editing event in the view controller, which would immediately re-open the keyboard.. with no visual change.
-(IBAction) textFieldDoneEditing : (id) sender{
[sender resignFirstResponder];
[sender becomeFirstResponder];
}
Xamarin / C# equivalent.. off the top of my head.
txtMyTextBox.Ended += (sender, e) =>
{
txtMyTextBox.ResignFirstResponder();
txtMyTextBox.BecomeFirstResponder();
};
Ah, interesting question and you have already known about the property "ShouldEndEditing" in UITextFieldDelegate, so why don't you try to implement a custom delegate for your UITextField?
I wrote a sample for you and I use a UISwitch to simulate the condition for hiding the keyboard, in your ViewController, use the code below:
public override void ViewDidLoad ()
{
MYTextFieldDelegate myDel = new MYTextFieldDelegate ();
UITextField textTF = new UITextField ();
textTF.Frame = new CoreGraphics.CGRect (50, 50, 200, 40);
textTF.BackgroundColor = UIColor.Red;
textTF.Delegate = myDel;
this.Add (textTF);
UIButton btnTest = new UIButton (UIButtonType.System);
btnTest.SetTitle ("Test", UIControlState.Normal);
btnTest.Frame = new CoreGraphics.CGRect (50, 100, 200, 40);
btnTest.TouchUpInside += delegate {
this.View.EndEditing (true);
};
this.Add (btnTest);
UISwitch keyboardSwitch = new UISwitch ();
keyboardSwitch.Frame = new CoreGraphics.CGRect (50, 150, 200, 40);
keyboardSwitch.ValueChanged += (sender, e) => {
bool flag = (sender as UISwitch).On;
myDel.FlagForDisplayKeyboard = flag;
};
this.Add (keyboardSwitch);
}
And this is MYTextFieldDelegate.cs:
class MYTextFieldDelegate : UITextFieldDelegate
{
public bool FlagForDisplayKeyboard { get; set; }
public override bool ShouldEndEditing (UITextField textField)
{
return FlagForDisplayKeyboard;
}
public MYTextFieldDelegate ()
{
FlagForDisplayKeyboard = false;
}
}
Hope it can help you.
Stuart's N-06 Books sample is good for getting basic understanding about using MvxSimpleTableViewSource.
[Register("FirstView")]
public class FirstView : MvxViewController
{
public override void ViewDidLoad()
{
View = new UIView(){ BackgroundColor = UIColor.White};
base.ViewDidLoad();
// ios7 layout
if (RespondsToSelector(new Selector("edgesForExtendedLayout")))
EdgesForExtendedLayout = UIRectEdge.None;
var textField = new UITextField(new RectangleF(10, 10, 300, 40));
Add(textField);
var tableView = new UITableView(new RectangleF(0, 50, 320, 500), UITableViewStyle.Plain);
Add(tableView);
tableView.RowHeight = 88;
var source = new MvxSimpleTableViewSource(tableView, BookCell.Key, BookCell.Key);
tableView.Source = source;
var set = this.CreateBindingSet<FirstView, Core.ViewModels.FirstViewModel>();
set.Bind(textField).To(vm => vm.SearchTerm);
set.Bind(source).To(vm => vm.Results);
set.Apply();
tableView.ReloadData();
}
}
But how can resize the tableview's height according it's content once it loads data?
Not entirely sure what you want to do... Normally in an iOS UI, the tableview size is fixed regardless of its content.
However, if you did want to resize the table then you could:
Inherit from MvxTableViewSource or UITableView and provide some logic there
Or add a binding in your class to some View property TableCount, bind that property and then implement the sizing logic there. Something like:
set.Bind(this).For(v => v.TableCount).To(vm => vm.Results.Count);
private int _tableCount
public int TableCount {
get { return _tableCount; }
set {
// implement your sizing animations here (maybe animate constraints?)
}
}
Just to add to Stuart's answer, here is an example for the frame of the table:
int _tableHeight;
public int TableHeight
{
get { return _tableHeight; }
set
{
_tableHeight = value;
_myPlayers.Frame = _tableHeight > 0 ? new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, Dimens.TableRowHeight * _tableHeight) : new CGRect(0, 0, UIScreen.MainScreen.Bounds.Width, Dimens.TableRowHeight * BusinessConstants.GetBiggestPositionCount());
_myPlayers.ReloadData();
}
}
I need to be able to hide controls on a page that uses constraints and remove the empty space that Hidden=true leaves. It needs to be similar to how the web handles visibility. If it's invisible, it doesn't take up space.
Does anyone know of a clean way to accomplish this?
Please let me know if you need more details.
Thx
Example:
UIButton | UIButton | UIButton
"empty space for hidden UIButton"
UIButton
That should really be rendered like this:
UIButton | UIButton | UIButton
UIButton
Edit: I'm using Xamarin Studio and VS2012 for development.
Since original question is related to Xamarin, I provide complete C# solution.
First, create height constraint for your view and give it an identifier in Xcode Interface Builder:
Then in controller override ViewDidAppear() method and wrap view with HidingViewHolder:
public override void ViewDidAppear(bool animated)
{
base.ViewDidAppear(animated);
applePaymentViewHolder = new HidingViewHolder(ApplePaymentFormView, "ApplePaymentFormViewHeightConstraint");
}
It is important to create HidingViewHolder when view was laid out, so it has real height assigned.
To hide or show view you can use corresponding methods:
applePaymentViewHolder.HideView();
applePaymentViewHolder.ShowView();
HidingViewHolder source:
using System;
using System.Linq;
using UIKit;
/// <summary>
/// Helps to hide UIView and remove blank space occupied by invisible view
/// </summary>
public class HidingViewHolder
{
private readonly UIView view;
private readonly NSLayoutConstraint heightConstraint;
private nfloat viewHeight;
public HidingViewHolder(UIView view, string heightConstraintId)
{
this.view = view;
this.heightConstraint = view
.GetConstraintsAffectingLayout(UILayoutConstraintAxis.Vertical)
.SingleOrDefault(x => heightConstraintId == x.GetIdentifier());
this.viewHeight = heightConstraint != null ? heightConstraint.Constant : 0;
}
public void ShowView()
{
if (!view.Hidden)
{
return;
}
if (heightConstraint != null)
{
heightConstraint.Active = true;
heightConstraint.Constant = viewHeight;
}
view.Hidden = false;
}
public void HideView()
{
if (view.Hidden)
{
return;
}
if (heightConstraint != null)
{
viewHeight = heightConstraint.Constant;
heightConstraint.Active = true;
heightConstraint.Constant = 0;
}
view.Hidden = true;
}
}
In storyboard wire your constrains first. Then try this
self.viewToHideHeight.constant = 0;
self.lowerButtonHeightFromTop.constant = self.viewToHideHeightFromTop.constant + self.viewToHideHeight.constant;
[UIView animateWithDuration:0.5f animations:^{
self.viewToHide.alpha = 0.0f;
[self.view layoutIfNeeded];
}];
I'm currently trying to create a custom navigation-bar for my navigation-controller in iOS using MonoTouch. The application I'm developing has the need to have a single string available on-screen no matter where you are on the application, and I wan't to achieve something like this (I know this is a little out off proportion, but you should be able to get the point):
+-----------------------------------------+
| /---------| +---+ |
| / Back | Controller Name |btn| |
| \---------| +---+ |
+-----------------------------------------+
| Current building <- global string |
+-----------------------------------------+
The bottom bar should be as small as possible (while not being hard to read), and the original navigation-bar might need to be a tiny bit smaller than normal.
Also, I've tried not to use the designer, but to write all the UI-code myself, but if that is impossible to achieve this, then I'll off cause have to use the designer. Currently I've found a project for creating a custom UINavigationBar in MonoTouch, but I have no idea of how to apply that to my NavigationController (see as the NavigationBar-property is read-only). The project I was talking about can be found here: https://github.com/mafis/Monotouch-Custom-Control/tree/master/CustomControls. Also, I would like the design of the actual navigationbar (the top part) to be standard iOS design, and work as a navigationbar normally would.
Any hints, or pointers at how to do this would be appreciated.
This is how I ended up solving this problem. I subclassed UIViewController like this:
using System;
using MonoTouch.UIKit;
using MonoTouch.CoreGraphics;
using System.Drawing;
using System.Collections.Generic;
using FdvWeb.Core;
namespace FdvWeb
{
[MonoTouch.Foundation.Preserve(AllMembers=true)]
public class MainNavigationController : UINavigationController
{
private const float NAV_BAR_HEIGHT = 44;
private readonly float buildingBarTop = 44;
private readonly float buildingBarHeight = 17;
private readonly float viewOffset;
private readonly HashSet<UIViewController> modifiedViewControllers = new HashSet<UIViewController>();
UITextView buildingTextView;
[MonoTouch.Foundation.Preserve]
public MainNavigationController ()
{
viewOffset = buildingBarTop + buildingBarHeight - NAV_BAR_HEIGHT;
var tt = new UITextView (new RectangleF (0, buildingBarTop, 320, buildingBarHeight));
NavigationBarHidden = false;
NavigationBar.AddSubview (tt);
tt.Font = UIFont.BoldSystemFontOfSize (12);
tt.TextAlignment = UITextAlignment.Center;
tt.TextColor = UIColor.LightTextColor;
tt.BackgroundColor = UIColor.ViewFlipsideBackgroundColor;
tt.Editable = false;
tt.ContentInset = new UIEdgeInsets (-9, 0, 0, 0);
buildingTextView = tt;
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
}
public override void ViewWillAppear (bool animated)
{
base.ViewWillAppear (animated);
}
public string BuildingText
{
get {
return buildingTextView.Text;
}
set {
container.Resolve<IUIThreadDispatcher> ().DispatchOnUIThread (delegate { // Run on UI thread
buildingTextView.Text = value;
});
}
}
public override void PushViewController (UIViewController viewController, bool animated)
{
if (!modifiedViewControllers.Contains (viewController))
{
viewController.View = new PaddedView (viewController.View, new InnsetF (0, viewOffset, 0, 0));
modifiedViewControllers.Add (viewController);
}
viewController.NavigationItem.RightBarButtonItem = pickBuildingItem;
base.PushViewController (viewController, animated);
}
private class PaddedView : UIView
{
private UIView view;
private InnsetF innsets;
public PaddedView (UIView view, InnsetF innsets)
: base(view.Frame)
{
this.view = view;
this.innsets = innsets;
this.AutoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight;
this.AddSubview (view);
}
public override void LayoutSubviews ()
{
//apply the insets to the subview
view.Frame = new RectangleF (innsets.Left, innsets.Top,
Frame.Size.Width - innsets.Left - innsets.Right,
Frame.Size.Height - innsets.Top - innsets.Bottom);
}
}
private class InnsetF
{
private float top, bottom, left, right;
public InnsetF (float left, float top, float right, float bottom)
{
this.top = top;
this.left = left;
this.bottom = bottom;
this.right = right;
}
public float Top
{
get { return top; }
set { top = value; }
}
public float Bottom
{
get { return bottom; }
set { bottom = value; }
}
public float Right
{
get { return right; }
set { right = value; }
}
public float Left
{
get { return left; }
set { left = value; }
}
}
}
}
So you want the reverse of what Spotify does when it is offline (the string message you're referring to appears above the UINavigationController).
I don't think the approach you're taking is going to work - as far as I know, the NavigationBar can only be 44 pixels high. Could you take a similar approach to Spotify and move it above the NavigationBar? This way you could leave the NavigationController etc as is and simply make it's view smaller, then add the string to the window object - that way it will persist as you navigate through your application.