So I'm trying out Xamarin Forms, and I made a control that has a renderer that outputs a UICollectionView. The collection view in question was ported from an Objective C version. In the Objective C version, I implemented collectionView:didSelectItemAtIndexPath: and when I clicked on the item that method was called. But in the Xamarin version, this does not seem to be the case and the ItemSelected method is not called. I've tried using both the Delegate and WeakDelegate versions to no avail. I made sure AllowsSelection is true.
I tried adding a UIButton to the cell, and was able to get a log entry from its TouchUpInside handler, so I don't think it's a matter of another view on top stealing the touches. Pans and such work. And in Simulator I had it highlight the drawn layers and didn't notice anything fishy.
Anyone have any ideas of stuff to try? Thanks.
I don't know if this can help you, but i get the same problem using the GridView control of the Xamarin.Forms.Labs.
I see that in delegate, the method "ItemSelected" is not called, but is call the "ItemHighlighted" method. So I used this to make the GridView selectitem works.
I hope this can be helpfull. :)
Double check that you have assigned the UICollectionViewSource object to ColelctionView.Source and NOT ColelctionView.DataSource by mistake
I did eventually fix this. What was happening is that Xamarin.Forms adds a gesture recognizer to the root of the page which cancels touches in the page. This works well in general, but does not work well in the case of UICollectionViews where it interferes with the UICollectionView calling ItemSelected.
What I ended up doing was to create this custom renderer:
public class IosTapFixPageRenderer : PageRenderer {
public override void ViewDidLoad()
{
base.ViewDidLoad();
foreach (var g in View.GestureRecognizers) {
g.CancelsTouchesInView = false;
}
}
}
Then I assigned this as the renderer for problem pages containing UICollectionViews using the usual attribute method:
[assembly: ExportRenderer(typeof(CalendarPage), typeof(IosTapFixPageRenderer))]
Related
I am creating a framework for the other apps to use it. I want to find when the display presented to the user changes. These changes include addition and removal of subviews, scrolling down, adding text, etc. Is there a way I can directly check when the content presented on the screen is changing. Above question is a part of the problem.
Did you mean viewDidLoad?
That function called first time after all view loaded same as ViewTreeObserver.OnGlobalLayoutListener.
After you explanations I would simply do something like this:
class MyViewController:UIScrollViewDelegate{
func addSubview(){
self.takeSnaphot()
}
func scrollViewDidScroll(scrollView:UIScrollView){
self.takeSnaphot()
}
func takeSnaphot(){
//the code to take snaphots
}
}
I'm working on iOS 8 custom keyboard extension right now, and there are some issues that I cannot figure out.
First, I think the UITextInputDelegate Methods are not working as I expected.
Does this sound right: selectionWillChange: and selectionDidChange: methods should be called when user long-presses typing area? And textWillChange: and textDidChange: methods should be called whenever the text is literally changing?
Actually, what I observed is that, when I changed selection in text input area, textWillChange: and textDidChange: are called, and I cannot get a clue that the other two methods are called in what condition. If anyone knows about the usage of these delegate methods, please let me know.
Second, I know the playInputClick: method can be used to virtualize keyboard click sound in custom keyboard. As this is applicable in a normal situation, I found it impossible to apply in iOS 8 custom keyboard extension. My app consists of one keyboard view controller, and custom view that subclasses UIView is added to this view controller. My approach is that UIInputViewAudioFeedback delegate is declared in this custom view, enableInputClicksWhenVisible method is returning YES, class method that calls [[UIDevice currentDevice] playInputClick] is set, then this method is called wherever the keyboard sound is needed: which is not working at all.
Is my approach is wrong in any way? If anyone has succeeded in using playInputClick method, please share your wisdom.
Thank you
It is best to play the Audio on a queue rather than in the UI key handler
func playPressKeySound() {
let PRESS_KEY_DEFAULT_SOUND_ID: SystemSoundID = 1104
dispatch_async(dispatch_get_main_queue()) {
AudioServicesPlaySystemSound(PRESS_KEY_DEFAULT_SOUND_ID)
}
}
NOTE: this only works if the keyboard has Full Access switched on, so best test there is full access before calling, otherwise the keyboard can suffer long pauses.
For the second question, try AudioServicesPlaySystemSound
#define PRESS_KEY_DEFAULT_SOUND_ID 1104
- (void)playPressKeySound {
if (self.openPressSound) {
AudioServicesPlaySystemSound(PRESS_KEY_DEFAULT_SOUND_ID);
}
}
I have an iOS app I'm working on using Xamarin and MVVMCross, but I am also using a third-party native library which includes some views of it's own (loaded from .xib files with the implementation in the library). What I need to do is set some properties on those native views and I'm trying to see if there's a way to do it that doesn't involve jumping into xcode and trying to recompile that whole thing (because I can't get that working at the moment).
So my question is, is there a way to intercept, application-wide, all attempts to load a view so that I can examine the view and if it's one of those from the third-party library, set some properties on it before it's displayed?
MvvmCross has a MvxTouchViewPresenter which has a ChangePresentation property, but it seems to only apply to MvxViewController loaded by MvvmCross itself.
You can very easily intercept all attempts to access a viewmodel by overriding the Show() method on your MvxTouchPresenter. For example:
public override void Show(MvxViewModelRequest request)
{
IMvxTouchView view = this.CreateViewControllerFor(request);
UIViewController viewController = (UIViewController) view;
this.Show(view);
}
You can then examine all Views in the UIView heirarchy by using something similar to the Objective-C code in this post. You just need to walk through all the UIViews in the viewController property and identify your view (perhaps by "smelling it" with respondsToSelector; I can't figure out exactly how you'd use isKindOfClass if Xamarin doesn't know it).
I hope I understood your question. Let me know if there's anything else missing.
I am attempting to structure my application using the MVVM pattern. Therefor I have ViewModels that raise events when data changes, and the UI is expected to react to those events and update the visible UI controls.
I have a derived UITableViewCell that gets initialized with a certain ViewModel every time a new cell is created or dequeued (very similar to miguel's example here). One main difference being part of the initializing relies on subscribing to an event of the ViewModel. This creates a reference from the long lived ViewModel to this specific cell, holding it in memory for the lifetime of the ViewModel. When the cell is re-used, the old subscription is cleaned up and a new one created to the new ViewModel, that works fine.
However the problem is there doesn't seem to be any opportunity to clean up the last subscription once the cell is completely finished, which means it is held in memory for the lifetime of the ViewModel (much longer than I want it to be).
'Completely finished' depends on the VC hierarchy, but in this case the derived DialogViewController that contains the TableView with custom cells has been popped from the UINavigationController stack and has been disposed.
willMoveToSuperview is never called (I was hoping it would be with 'null' being passed in).
removeFromSuperview is never called.
Dispose on each cell is never called.
Disposing of the UITableViewController doesn't dispose each cell.
Disposing of the TableView within the controller doesn't even dispose each cell.
The only way I can manually dispose each cell (and hence clean up subscriptions) is by enumerating the cells manually myself in each of my derived UIViewControllers, something I want to avoid.
Has anyone has similar troubles like this? I cant be the first using the MVVM pattern with UITableViewCells.
Is this a bug with the Dispose pattern in the base MonoTouch UIKit wrappers?
EDIT:
Here is cut-down version of one of the custom UITableViewCells. Note I'm taking a pragmatic approach where I explicitly subscribe to events of properties I know may change, not a full MVVM bind every property to the UI. So my binding code consists of just standard event subscriptions:
public class MyCustomCell : UITableViewCell
{
private InvoiceViewModel currentViewModel;
private readonly UILabel label1;
private readonly UILabel label2;
public MyCustomCell(NSString reuseId)
: base(UITableViewCellStyle.Default, reuseId)
{
Accessory = UITableViewCellAccessory.DisclosureIndicator;
SelectedBackgroundView = new UIView()
{
BackgroundColor = UIColor.FromRGB(235,235,235),
};
label1 = new UILabel();
ContentView.Add(label1);
// The rest of the UI setup...
}
public void Update(MyViewModel viewModel)
{
if ( currentViewModel == viewModel )
return;
if ( currentViewModel != null )
{
// Cleanup old bindings.
currentViewModel.UnacknowledgedRemindersChanged -= HandleNotificationsChanged;
}
currentViewModel = viewModel;
if ( viewModel != null )
{
viewModel.UnacknowledgedRemindersChanged += HandleNotificationsChanged;
label1.Text = viewModel.SomeProperty;
// Update the rest of the UI with the current view model.
}
}
private void HandleNotificationsChanged()
{
// Event can fire on background thread.
BeginInvokeOnMainThread(() =>
{
// Relevant UI updates go here.
});
}
protected override void Dispose(bool disposing)
{
// Unsubscribes from ViewModel events.
Update(null);
base.Dispose(disposing);
}
}
And my derived MT.D element class has a 1:1 element:viewmodel, so the GetCell method looks like this:
public override UITableViewCell GetCell (UITableView tv)
{
var cell = (MyCustomCell) tv.DequeueReusableCell(key);
if (cell == null)
cell = new MyCustomCell(key);
cell.Update(viewModel);
return cell;
}
You're definitely not the first to do Mvvm table cells with MonoTouch.
I've blogged about it recently at http://slodge.blogspot.co.uk/2013/01/uitableviewcell-using-xib-editor.html
Before that there have been projects at NDC (search for "Flights of Norway") and there was a long running Mvvm project built on top of MonoTouch.Dialog.
In MvvmCross apps, we use tables bound to ObservableCollections and to other IList classes a lot.
Within these we generally don't hit many problems with left-living references, but that's because we generally don't encourage people to use long-living ViewModels - we try to create a new instance of ViewModel data to go with each View. However, I do understand that that may not be suitable for all appications.
When an Mvx user finds themselves with this type of problem, then some of the approaches we've tried are:
in iOS5 we did use the ViewDidUnload method to clean up bindings - but obviously that is now gone in iOS6.
depending on the UI presentation style (modal, splitview, navigationcontroller, popup, etc) we have tried manually detecting when views are 'popped' and using this to clear up bindings
again depending on the UI presentation style, we have tried using the ViewDidAppear, ViewDidDisappear events to add and tidy up the bindings
for all UIKit classes with data-binding we've always used Dispose as an extra place to try to clear up bindings
we've looked at using WeakReferences (especially from the ViewModel to the View) in order to get around the issues where the UIKit objects are owned by both iOS/ObjC and by MonoTouch/.Net - these problems are particularly hard to debug.
this weak reference code is likely to be one the key changes in the next release.
An example discussion about this (in Droid rather than in Touch) is on https://github.com/slodge/MvvmCross/issues/17
I'm sorry I can't offer you any specific advice at this moment. If you post some more example code about how you are creating and storing your bindings, I might be able to help more - but I can't really visualise what bindings you are creating right now.
I've got a few more links I'll add to this answer later - on mobile at present - too hard to add them here!
Update - for some more explanation on the WeakReference ideas, this is where we're heading now in v3 for MvvmCross - https://github.com/slodge/MvvmCross/tree/vNextDialog/Cirrious/Cirrious.MvvmCross.Binding/WeakSubscription - basically the idea is to use disposable weakreference event subscriptions - which won't keep the UIKit objects in RAM. It's not properly tested code yet. When it is, then I'll blog and talk about it more fully!
There are several ways to handle event in MonoTouch. It looks to me that mapping the event in IB is the most reliable way to do. What I don't understand is why sometimes the event mapped in ViewDidLoad doesn't work. For example, I have a UITextField (called tfCode). If it's mapped in IB for EditingDidEnd to tfCodeChanged, it works:
partial void tfCodeChanged(NSObject sender)
{
...
}
However, in ViewDidLoad, if I put in the following code, it doesn't get hit:
tfCode.EditingDidEnd += delegate {
...
};
But in general I'm doing a lot of event handling in ViewDidLoad and they mostly work.
So, I'm confused. Can anybody explain why?
The events are triggered as long as you do not override the internal handlers by assigning to either the Delegate or WeakDelegate properties.