A Hello World Today extension does not appear - objective-c

I'm quite new to iOS and I hit 2 problems when writing a hello world Today extension for iOS8. I tried to create a simple today extension using the template and it works good.
However if I tried 1 of the following, the widget does not show up in Today app (there's only a title of my extension, but no body):
Delete the "hello world" label coming with the template, and add 1 new label with "hello world 2"; I notice the constraints are also automatically deleted if I'm doing this.
Uncheck "Use Auto Layout" in storyboard of today view controller, create a property of UILabel and link to the label in storyboard, then call setText on the label on viewDidLoad:
TodayViewController.m:
#interface TodayViewController () <NCWidgetProviding>
#property (nonatomic, weak) IBOutlet UILabel *cityLabel;
#end
#implementation TodayViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
[self _updateTodayView];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
// Perform any setup necessary in order to update the view.
[self _updateTodayView];
// If an error is encountered, use NCUpdateResultFailed
// If there's no update required, use NCUpdateResultNoData
// If there's an update, use NCUpdateResultNewData
completionHandler(NCUpdateResultNewData);
}
- (void) _updateTodayView {
[[self cityLabel] setText:#"Hello World 3"];
}
#end
I've been developing on Windows for years and I'm ashamed to be blocked by this issue for a whole day.
My environment:
OS X Yosemite.
Xcode 6.1

Related

Cocoa Subclassing weirdness

I'm trying to understand how Subclassing works in Cocoa.
I've created a new Cocoa Application Project in XCode 5.1.
I drag a new Custom View onto the main window.
I create a new Objective-C class CustomViewClass and set it as a Subclass of NSView. This generates the following :
CustomViewClass.h
#import <Cocoa/Cocoa.h>
#interface CustomViewClass : NSView
#end
CustomViewClass.m
#import "CustomViewClass.h"
#implementation CustomViewClass
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
NSLog(#"Custom View initialised");
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
// Drawing code here.
}
#end
Note that I added the NSLog(#"Custom View initialised"); line so I can track what is going on.
In interface Builder, I select the Custom View and within the Idenditiy Inspecter set it's custom Class to CustomView. Then I run the Application.
As expected I get a Custom View initialised message in the Console.
I do exactly the same with an NSTextField adding it to the window, creating a new class TextFieldClass and the NSTextField custom Class is to TextFieldClass. I also add a NSLog(#"Text Field initialised"); in the same place as above to track things.
However when I run the App, I only get the Custom View initialised message in the Console and not the NSLog(#"Text Field initialised");message.
So initially I think that NSTextField doesn't recieve the initWithFrame message when it is created. So I add an initialiser to TextFieldClass :
- (id)init {
self = [super init];
if (self) {
// Initialization code here.
NSLog(#"Text Field initialised");
}
return self;
}
However this still doesn't seem to get called.
I assumed therefore that NSTextField just wasn't being subclassed. However, when I add this method to TextFieldClass :
-(void)textDidChange:(NSNotification *)notification {
NSLog(#"My text changed");
}
Run the app and lo and behold, every time I type in the text field I get the My text changed message in the Console.
So my question is, what is going on here? How does the NSTextField get initialized and how can you override it's initialiser?
Why does the Custom View seem to act differently to the NSTextField?
Source code here
For your first question, NSTextFiled gets initialised via
- (id)initWithCoder:(NSCoder *)aDecoder
In this case, you have dragged a NSTextField from the palette and then changed the class to your custom text field class in the identity inspector. Hence the initWithCoder: will be called instead of initWithFrame:. The same is true for any object (other than Custom View) dragged from the palette
Instead, if you drag "Custom View" from the palette and change the class to your custom text field class, the initWithFrame: will be invoked.
The CustomViewClass you have created is the second case, hence initWithFrame: is invoked. The TextFieldClass is the first case, hence initWithCoder: is invoked.
If you use the Interface Builder in XCode, you should use awakeFromNib to initialise your subclass.
- (void)awakeFromNib
{
// Your init code here.
}
If you want to use your subclass programatically and using the interface builder, then use code like this:
- (id)initWithFrame:(NSRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initView];
}
return self;
}
- (void)awakeFromNib
{
[self initView];
}
- (void)initView
{
// Your init code here
}

Objective-C: Grab title of web page from WebView

I'm trying to retrieve the title of a web page and display it in the NSWindows title. This application is document based and I haven't tried anything in a standalone application with an AppDelegate. How would I go forth and retrieve the title and display it in the window?
UPDATE: Here's my code (Note: Doesn't work quite yet)
TitleWindow.h
#import <Cocoa/Cocoa.h>
#import <WebKit/WebKit.h>
#interface TitleWindow : NSWindow <NSApplicationDelegate> {
}
#property
(retain, nonatomic) IBOutlet NSWindow *displayTitle;
#end
TitleWindow.m
#import "TitleWindow.h"
#import <WebKit/WebKit.h>
#implementation TitleWindow
- (void)displayTitle:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
{
if (frame == [sender mainFrame]){
[[sender window] setTitle:title];
}
}
#end
To get an NSWebViews page title.
You use it's mainFrameTitle
NSString * pageTitleString =[_webView mainFrameTitle];
NSLog(#"%#" , pageTitleString);
Read the Docs
example 2.
WebFrameLoadDelegate_Protocol
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)webFrame{
[_window setTitle:[_webView mainFrameTitle]];
}
UPDATE 2
I have not really built a document based app before. So setting one up to display a web view was fun :-P.
Which leads me to think the problem is two fold.
The first thing I released is just adding the example 2 code to the document.m is not enough.
The webview needs a delegate.
What worked for me and may not be (probably not) the right way to do it was in the Document.xib with the webview selected.
The Connections Inspector at the top shows you the frameLoadDelegate.
I connected this to the file's Owner by dragging it over to the file's Owner in the Place holders pane.
This worked.
And each time I changed the webpage from within the document the title changed along with it.
The second thing to note is I think the real method of setting the documents title is:
[self setDisplayName:#"a title"];
which works when set in:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
// Add any code here that needs to be executed once the windowController has loaded the document's window.
NSURLRequest* request = [NSURLRequest requestWithURL:_theUrls];
[[_webView mainFrame] loadRequest:request];
[self setDisplayName:#"a title"];
}
And a normal string, But if I change it to:
[self setDisplayName:[_webView mainFrameTitle]];
It does not work as the frame is not loaded yet so there is no title.
So you would think then that [self setDisplayName:[_webView mainFrameTitle]]; would work in the WebFrameLoadDelegate. No it does not. And I am not sure why.
So for now you may want to use the code in example 2 code.

Objective-C first launch code is ignored

I'm VERY new to Xcode but I am looking to add some extra functionality to my basic app. This functionality should log a string upon first launch. For debugging purposes I have also asked the program to output a string even if it's not first launch. I can't seem to find these messages anywhere (I'm assuming Xcode would snap to them like most IDEs). Am I doing this in the right file? The application uses the tab bar controller.
Thanks
//
// ViewController.m
//
// Created by Joel Kidd on 29/05/2013.
//
//
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"FirstLaunch"]) {
NSLog(#"This is not the first launch.");
} else {
// Place first launch code here
NSLog(#"This is the first launch!");
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"FirstLaunch"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
All NSLog messages should appear into the debug area panel (View>Debug Area>Activate Console), also application:didFinishLaunchingWithOptions: of your app delegate would be the ideal place to check for a first launch (on your example, the code would be executed each time your controller's view would be loaded).

ViewControllers with TextViews in UIPageViewController

I was trying to learn UIPageViewControllers and hit an Issue which I couldn't resolve.
This is what I tried to do:
Steps:
I simply created 2 view controllers and a page view controller in
StoryBoard.
Then I added some code to the File's Owner of PageViewController to
behave as a dataSource and delegate to itself.
When I ran, things worked well.
I added some buttons, and text fields to the second view controller.
I ran, worked well.
Now I added a text view to the second view controller and ran. When I tried to write something inside the text view, the page control jittered and moved to first view controller.
Has anyone experience this ever?
#interface AMPageViewController : UIPageViewController <UIPageViewControllerDataSource, UIPageViewControllerDelegate>
#end
The implementation:
#import "AMPageViewController.h"
#interface AMPageViewController ()
{
UIViewController *mainController;
UIViewController* socController;
}
#end
#implementation AMPageViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:#"MainStoryboard"
bundle: nil];
mainController = (UIViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: #"First"];
socController = (UIViewController*)[mainStoryboard instantiateViewControllerWithIdentifier: #"Second"];
[self setViewControllers:#[mainController]
direction:UIPageViewControllerNavigationDirectionForward
animated:NO
completion:nil];
self.dataSource = self;
self.delegate = self;
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerBeforeViewController:(UIViewController *)viewController
{
if (viewController == socController )
return mainController;
else return nil;
}
- (UIViewController *)pageViewController:(UIPageViewController *)pageViewController viewControllerAfterViewController:(UIViewController *)viewController
{
if (viewController == mainController )
return socController;
else return nil;
}
- (NSInteger)presentationCountForPageViewController:(UIPageViewController *)pageViewController
{
return 2;
}
- (NSInteger)presentationIndexForPageViewController:(UIPageViewController *)pageViewController
{
return 0;
}
#end
If you want to download and try the project
I've investigated a lot on this problem.
It seems a bug related to the internal (private) UIScrollView of the UIPageViewController.
If you search on StackOverflow you will find a lot of post with this problem and no solutions...
I seems that the UITextView (which is an UIScrollView and, AFAIR, has an internal UIWebView), sends some strange message to it's superviews chain, that makes the private UIScrollView of the UIPageViewController scrolling to the top-left corner.
I would have tried to block this message using method swizzling, but this is probably not ok for AppStore. So I tried other things.
The final solution is very simple: simply, embed your UITextView inside an UIScrollView!
This is a link to your project updated
If you do so, you'll solve the problem!
Try and let me know
EDIT:
How did I arrive to this solution:
An intuition.
A lot of debug and stack traces had make me think that the problem was related to a bug in the "nesting UIScrollView" system and some messages sent from the inner view to its superview.
UITextView inherits from UIScrollView and has inside an UIWebDocumentView (private) which is another UIScrollView. During the debug I saw a lot of messages (private methods) like "relayout superview" sent to the upper UIScrollView's. So, for some reason, the inner scroll view (UIWebDocumentView?) was sending a message/event to it's superview. This message/event (probably because of a bug) was not stopping to the external UITextView, and was forwarded to the UIScrollView handled by UIPageViewController.
Embedding the UITextView inside a simple UIView was not enough, because UIView forward the message to it's superview if it can't handle.
I thought: UIScrollView probably doesn't (otherwise it wouldn't simple to nest UIScrollViews), so I tried and it worked.
This is all a supposition because I stopped inspecting, I will have a more in-depth look this week.
Build target iOS-7.0.
The scrollview trick wasn't working for me. Tried to embed the textview in a scrollview through storyboard and code but no luck.
Simply delaying the call to the textview did it. Not very elegant, but its the only thing I've gotten to work so far.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self.textView becomeFirstResponder];
});
}
Tested, working on my iPhone 5 and my ultra-slow iPhone4. Although its totally possible that whatever implementation detail enables the textview to become the responder could take longer than the set time. So keep in mind this isn't exactly bulletproof.
--EDIT--
Well... it's working on my iPhone 4 beater with a delay of 0.0000000000000001
you did not set before and after view controllers and also look in to first responder for socController

Objective-C - Scrollview Events

I found some answer that quite solve my questions, but seems that I'm missing some steps. I'm just trying to intercept the "ViewDidBlablabla" events of a ScrollView, so I created a custom UIScrollView class.
MyScrollView.m
#import "MyScrollView.h"
#implementation MyScrollView
- (id)initWithFrame:(CGRect)frame{
NSLog(#"initWithFrame");
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
-(void)scrollViewWillBeginDragging:(UIScrollView*)scrollView{
NSLog(#"scrollViewWillBeginDragging");
//scrollView.contentOffset
}
-(void)scrollViewDidScroll:(UIScrollView*)scrollView{
NSLog(#"scrollViewDidScroll");
}
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView{
NSLog(#"scrollViewDidEndScrollingAnimation");
}
#end
MyScrollView.h
#import <UIKit/UIKit.h>
#interface MyScrollView : UIScrollView <UIScrollViewDelegate>
#end
When I alloc my custom Scroll View I do get the console message "initWithFrame", but there's no way to get to the other events. What am I missing?
If you need some other pieces of code feel free to ask, but since I get to my custom "initWithFrame" method, I suppose that the error should be here.
Try setting your view as the delegate of itself:
if (self) {
// Initialization code
self.delegate = self;
}
Technically, you do not need to inherit UIScrollView to respond to scrolling events: it is sufficient to set the delegate to an implementation of <UIScrollViewDelegate>.
Also, the scrollViewDidEndScrollingAnimation: method has been gone for about two years, so it is not going to be called on modern iOS installations.