Custom URL Handling iOS xamarin - ios

I'm trying to set up my application so that I can add an NSUrl attribute to specified substrings of a textview, and when that NSUrl is touched, it pushes a new view rather than open a new url.
I am ovverriding OpenURL in my AppDelegate.cs class, and sending a notification with the url
public override bool OpenUrl (UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{
NSNotificationCenter.DefaultCenter.PostNotification (NSNotification.FromName ("ArtistDetail", url));
return true;
}
Then in my class that contains the the text view, I add the observer in the constructor
NSNotificationCenter.DefaultCenter.AddObserver ("ArtistDetail", delegate{artistDetail(null);});
And have my method
public void artistDetail (NSNotification notification)
{
//push view based on info in notification.object
}
I'm not having any luck. I found this article Can't push view controller after overriding openURL where someone did it in objective C the same way. One problem - I'm putting breakpoints and log notes in my OpenURL override, and they're not getting hit. When you click/tap an NSSUrl, does it by default call OpenUrl? If so, why isn't my override getting hit?

I've been researching how to do this for 6 hours. Of course, 20 minutes after I post the question on SO, I figure out the answer.
I had to implement a UIApplication subclass, override the method there, register it, and launch it as the UIApplication in my Main.cs.
UIApplicationMain.cs
[Register ("UIApplicationMain")]
public class UIApplicationMain : UIApplication
{
public UIApplicationMain ()
{
}
public override bool OpenUrl (NSUrl url)
{
NSNotificationCenter.DefaultCenter.PostNotification (NSNotification.FromName ("ArtistDetail", url));
return true;
}
}
Main.cs
public class Application
{
// This is the main entry point of the application.
static void Main (string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
UIApplication.Main (args, "UIApplicationMain", "AppDelegate");
}
}
View Controller where the hyperlinks are displayed, and should be handled dynamically to push the view controller you want
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
NSNotificationCenter.DefaultCenter.AddObserver ("ArtistDetail", artistDetail);
}
public void artistDetail (NSNotification notification)
{
NSUrl artistName = (NSUrl)notification.Object;
String name = artistName.AbsoluteString;
this.NavigationController.PushViewController (new ArtistDetailViewController (name), true);
}
For anyone trying to solve the problem of "How to put a button within your UITextView"
(like twitter does with #hastags and #users) - this is it! Put the text in an NSAttributedString, apply the link attribute to the text you want to act as a button, and handle accordingly.

Related

ViewModel is null during ViewDidLoad

I am getting started with MvvmCross in iOS.
public class MainView : MvxTabBarViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
var vm = (MainViewModel)this.ViewModel;
if (vm == null)
return;
}
}
Setting a breakpoint to the line where access the ViewModel, shows me, that ViewModel is null.
I can workaround this by calling ViewDidLoad() in the constructor. Then, ViewModel is null during the constructor call, but valid in the default ViewDidLoad call. But that looks like a workaround. can anybody help?
I'm guessing here the problem here will be specific to the way that TabBarViewController is constructed.
ViewDidLoad is a virtual method and it is called the first time the View is accessed.
In the case of TabBarViewController this happens during the iOS base View constructor - i.e. it occurs before the class itself has had its constructor called.
The only way around this I've found is to add a check against the situation in ViewDidLoad, and to make a second call to ViewDidLoad during the class constructor.
You can see this in action N-25 - https://github.com/MvvmCross/NPlus1DaysOfMvvmCross/blob/976ede3aafd3a7c6e06717ee48a9a45f08eedcd0/N-25-Tabbed/Tabbed.Touch/Views/FirstView.cs#L17
Something like:
public class MainView : MvxTabBarViewController
{
private bool _constructed;
public MainView()
{
_constructed = true;
// need this additional call to ViewDidLoad because UIkit creates the view before the C# hierarchy has been constructed
ViewDidLoad();
}
public override void ViewDidLoad()
{
if (!_constructed)
return;
base.ViewDidLoad();
var vm = (MainViewModel)this.ViewModel;
if (vm == null)
return;
}
}

StoryBoard.InstantiateViewController giving "Unknown class testController in Interface Builder file"

I am working on a Xamarin IOS/monotouch project.
I have been stuck with this error since some time. These are the lines of code
var prfc = this.Storyboard.InstantiateViewController ("testController") as testController;
if (prfc != null) {
this.PresentViewController(prfc,false,null);}
I tried renaming viewcontroller, cleaning and building solution, deleting viewcontroller and adding a new one with another name
I have ensured that all spellings are correct, i have same Viewcontroller name, StoryBoardID, and Restoration ID. This is how I have registered my viewcontroller in Designer partial class
[Register ("testController")]
partial class testController
Here is my appdelegate.cs code `[Register ("AppDelegate")] public partial class AppDelegate : UIApplicationDelegate { // class-level declarations UIWindow window; public static UIStoryboard Storyboard = UIStoryboard.FromName ("MainStoryboard", null); public static UIViewController initialViewController; public override UIWindow Window { get; set; }
`[Register ("AppDelegate")] public partial class AppDelegate : UIApplicationDelegate { // class-level declarations UIWindow window; public static UIStoryboard Storyboard = UIStoryboard.FromName ("MainStoryboard", null); public static UIViewController initialViewController; public override UIWindow Window { get; set; }
// This method is invoked when the application is about to move from active to inactive state.
// OpenGL applications should use this method to pause.
public override void OnResignActivation (UIApplication application)
{
}
// This method should be used to release shared resources and it should store the application state.
// If your application supports background exection this method is called instead of WillTerminate
// when the user quits.
public override void DidEnterBackground (UIApplication application)
{
}
// This method is called as part of the transiton from background to active state.
public override void WillEnterForeground (UIApplication application)
{
}
// This method is called when the application is about to terminate. Save data, if needed.
public override void WillTerminate (UIApplication application)
{
}
public override void FinishedLaunching (UIApplication application)
{
window = new UIWindow (UIScreen.MainScreen.Bounds);
initialViewController = Storyboard.InstantiateInitialViewController () as UIViewController;
window.RootViewController = initialViewController;
window.MakeKeyAndVisible ();
return ;
}
}`
I have tried out every possibility but can't fix this. This error is occuring for every new viewcontroller that I add to Storyboard. I am getting a null when I call InstantiateViewController. I have tried cleaning and rebuilding. It works on simulator, but not on device
The issue was as pointed out by #orkenstein. I was targeting 7.1 whereas was testing on a 7.0 device

Custom UIViewController not receiving orientation change notifications on device rotation

So, I have a custom UIWindow, a custom UIViewController, and a custom UIImageView.
private class CoachingWindow : UIWindow
{
public CoachingWindow(...)
{
RootViewController = new CoachingOverlayViewController(...);
}
private class CoachingOverlayViewController : UIViewController
{
public CoachingOverlayViewController(...)
{
View = new CoachingOverlayView(...);
}
public override void DidRotate (UIInterfaceOrientation fromInterfaceOrientation)
{
...
}
public override bool ShouldAutorotate()
{
return true;
}
private class CoachingOverlayView : UIImageView
{
public CoachingOverlayView(...)
{
...
}
}
}
}
The window, view controller, and view all display properly when first called: the custom UIWindow appears as an overlay over the rest of the existing UIWindows, the view controller is properly assigned as the RootViewController, the view is assigned to the View property of the view controller. So, it all renders correctly.
However, the overridden DidRotate() and ShouldAutorotate() methods never get get called when I physically rotate the device or simulator.
I'm thinking it may have something do with the fact that I'm using a custom UIWindow. Perhaps the window isn;t receiving notifications of orientation change from iOS? Or does iOS send these notifications directly to view controllers? Maybe I have to somehow make the view controller subscribe to these events because it's a custom view controller???
I'm using MonoTouch and iOS 6.
Any suggestions would be great. I'm beating my head against the wall here.
in iOS 6 you can use
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{ ... }
Figured it out. I had override ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation) instead of ShouldAutorotate(). But this is actually deprecated in iOS6. Still have to figure out how to do it the iOS6 way, but this will do for now.
public override bool ShouldAutorotateToInterfaceOrientation (UIInterfaceOrientation toInterfaceOrientation)
{
...
}

how to show optional screen instead of main screen at app start up

My app has has an option to show screen-B instead of screen-A (default main) at app start up.
First I tried pushScreen(screen-B) in screen-A's constructor which resulted in display stack has screen-A on top and then screen-B..
What I want to do is:
At start up if the option is on, show screen-B (stack has screen-B then screen-A so that Escape key would lead to screen-A)
What would be the right way to acheive this?
You might consider pushing B a little bit later in the process, in the onUiEngineAttached method:
class ScreenA extends Screen {
...
protected void onUiEngineAttached(boolean attached) {
if (attached) {
// check condition and push B as appropriate
}
}
}
When the application starts, in the UiApplication class make the following:
class UiApp extends UiApplication {
UiApp() {
if (yourCondition)
//start A
else
//start B
}
public static void main (String[] args) {
UiApp app = new UiApp();
app.enterEventDispatcher();
}
}

BlackBerry back to screen detection

HI, I try to detect reterning to screen after closing another screen,
should work when returning from my application screens, but also returning from device camera
after shooting video. In overriden method onExposed() I'm able to detect this situation,
but it's called too many times, and also called when dialog was shown (alert).
Is there better way to detect return to screen?
protected void onExposed() {
// return to screen detected
MainApp.addLog("onExposed");
}
returning from device camera after
shooting video
Check the Application.activate()
The system invokes this method when it
brings this application to the
foreground. By default, this method
does nothing. Override this method to
perform additional processing when
being brought to the foreground.
If you override the Screen.onUiEngineAttached(boolean) method, you can be notified when the screen is attached or detached from the UI --- basically when it's pushed or popped from the screen stack.
I had to do a similar thing and found it's very confusing because onExposed() can be called multiple times in uncertain timing.
To detect returning from screen B in screen A (main screen), I used screen B's onUiEngineAttached(false) which is called when it is popped.
To use callback:
public interface Ievent {
public void backFromScreenBEvent();
}
Screen A:
public class ScreenA extends MainScreen implements Ievent
{
private ScreenB screenB;
// constructor
public ScreenA()
{
screenB = new ScreenB(this); // pass over Ievent
// ....
}
public void backFromScreenBEvent()
{
// screen B is returning, do something
}
Screen B:
public final class ScreenB extends MainScreen
{
private Ievent event;
// constructor
public ScreenB(final Ievent event)
{
this.event = event;
// ...
}
protected void onUiEngineAttached(boolean attached) {
super.onUiEngineAttached(attached);
if (!attached) {
event.backFromScreenBEvent(); // notify event
}
}

Resources