'Shortcut' for actions on objects - ios

So I have 3 objects in my project right now:
_vLabel.hidden = YES;
_wLabel.hidden = YES;
_nextButton.hidden = YES;
As you can see I do the .hidden thing for all of them . Is there a way of creating variable or something with which I would only have to apply it instead writing the whole line, like:
_vLabel.hide;
Thanks in advance!

It seems silly to do it, as you should just follow the convention that any other iOS programmer expects and understands, but yes you can do it:
UIView+MyCategory.h:
#interface UIView (MyCategory)
-(void) hide;
#end
UIView+MyCategory.m:
#implementation UIView (MyCategory)
-(void) hide {
[self setHidden:YES];
}
#end
usage:
[myView hide];

I find the question unclear. What would be a "shortcut" in this situation? How can you get any "shorter" than this:
_vLabel.hidden = YES;
_wLabel.hidden = YES;
_nextButton.hidden = YES;
To me, the "length" here - the thing that needs shortening - is that you're doing something three times instead of once. If these objects are objects that you typically hide and show together, then it would be nice to have one method that hides and shows all of them:
- (void) toggle {
_vLabel.hidden = !_vLabel.hidden;
_wLabel.hidden = !_wLabel.hidden;
_nextButton.hidden = !_nextButton.hidden;
}
It's just as much code, but once you've written it, then every time you want to show them all or hide them all you just say:
self.toggle;
Which is itself actually a shortcut for:
[self toggle];
So what you'd be doing here is writing a method in order to change what the language a little, and that's a perfectly reasonable thing to do if it avoids repetition.

Hmm, if your only problem is that writing label.hidden = YES; is too long then why donĀ“t you just use code snippets? That way you keep using the naming conventions with less writing.
You can even use Xcode to create these snippets. Just select the text and drag it from the edge(this part can be pretty tricky) to the snippets area.
Personally though I would use something like TextExpander or aText or whatever else you prefer to do this. These apps are usually faster than Xcode and easier to use.
Just create in those apps snippet for .hidden = YES; and assign it to a shortcut like .hide
So now whenever you want to hide something you just write myLabel.hide TextExpander will expand it into
myLabel.hidden = YES;
And to make things visible again you could assign .show to .hidden = NO;

Related

self.property = self.property in objective c

Good morning, I am looking through some old code online in objective c and I am having trouble finding out what the following means.
The class is subclassing UIView and is following the UIScrollViewDelegate protocol. The following is the class method I have some questions about:
#property(nonatomic, strong) NSDate * _Nonnull date;
- (void)layoutSubviews {
[super layoutSubviews];
if(!self.pagingView) {
UIScrollView *pagingView = [[UIScrollView alloc] initWithFrame:self.bounds];
pagingView.pagingEnabled = YES;
pagingView.directionalLockEnabled = YES;
pagingView.delegate = self;
[pagingView setAutoresizingMask:UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight];
[self addSubview:pagingView];
self.pagingView = pagingView;
}
CGRect f = self.pagingView.bounds;
CGSize s = self.pagingView.contentSize;
if(s.width != f.size.width * 3) {
self.date = self.date;
}
}
First, I am not sure what the [super layoutSubviews] is achiving and I am not sure what the self.date = self.date is trying to do. Is it only setting itself with itself? If so, I am not able to get this to work with swift. (Which I am trying to convert the code to)
https://github.com/Daij-Djan/DDCalendarView/blob/master/DDCalendarView_objc/DDCalendarView.m
Thanks again for any help;
In Objective-C, dot notation is a shorthand (syntactic sugar) for property accessor method calls. What you've posted could be rewritten as:
if width != size.width * 3 {
[self setDate:[self date]];
}
In other words, the current value of the date property is being passed back into the date property setter. In typical code, this won't really do anything. Without seeing the implementation of the date setter (and/or getter) method, it's impossible to say why the code you're looking at is doing this. However, my guess is that the date setter has a side-effect that is triggered anytime it's called, so this is a convenient way to trigger that side-effect without changing the property's value.
Assuming this is the case, I would add that this is not very good code. At the very least, there should be a comment explaining what this is doing. Even better would be to break whatever side-effect is happening out into its own method, so that it can be called explicitly instead of relying on the date setter even in cases where the property value shouldn't be changed.
Looking at the code you posted, the setDate: method is performing some view setup, so doing self.date = self.date; is the author's way of forcing this setup to be done without changing the set date. This would be better done if that view setup code was factored out of setDate:, so that it would simply make a call to do that setup after a new date is set. Then in your above code, it could simply call that setup method, something like updateDateViews or something along those lines.
As for your question about the code calling [super layoutSubviews];, it is always a good idea any time you override a method to call the superclass implementation, unless you know for sure what the superclass implementation does and you know that you don't need to call it, and in rare cases specifically don't want to call it. So a good rule of thumb is to just always add the call to the superclass method. It's the same as when you override viewWillAppear:, viewWillDisappear:, etc. You should always be calling the superclass implementation for those.

Completely disable the swipe back gesture throughout the entire app

I want to disable the swipe back gesture that allows users to go back.
I've tried:
if ([self.navigationController respondsToSelector:#selector(interactivePopGestureRecognizer)]) {
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
}
and all sorts of other code I've found online, but none of them work at all? I'm using iOS 8.3.
Is there a way to disable this all together? Thank you.
First of all as #Fogmeister said in comments, you need to have a very good reason to remove this native function to your app.
Now, having said this, the solution:
SwipeBack and JRSwizzle (Needed by SwipeBack)
You can use this in a single ViewController in which you want to remove the functionality or you create a custom class for UINavigationController and you use it there:
#import <SwipeBack/SwipeBack.h>
- (void)viewWillAppear:(BOOL)animated
{
//For a single viewcontroller
self.navigationController.swipeBackEnabled = NO;
//If you are in the custom class
self.swipeBackEnabled = NO;
}
Hope it helps.

Get access to UIView/UIWindow for the little blue auto correct box

It seems like you can access most views/windows in iOS, but I don't have much experience, so hoping someone can help.
In trying to resolve this issue, I have some ideas, but they will require me to get the UIView used for the little blue box displayed when a word will be autocorrected.
I think I need to start with something like this, but have no idea where to go from here:
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *windows = [UIApplication sharedApplication].windows;
for (UIWindow *w in windows)
{
}
}
I'm adding this as a separate question because I can think of other reasons a person might want to access this (maybe change the color/style?). I understand it is part of the "system" and maybe not best practices to modify it, but I think it's probably okay as long as not a crucial part of your app, and you make your code pretty safe (i.e.-check pointers for nil, error on side of safety). In my case, for now, I just want to hide it, when it should be hidden (when scrolling).
Once I realized UIWindow was derived from UIView I came up with this possible answer, which seems to work so far:
-(void) walkViews:(UIView*)v
{
NSString *className = NSStringFromClass([v class]);
if([className isEqualToString:#"UIAutocorrectInlinePrompt"])
v.hidden = YES;
NSArray *subviews = v.subviews;
for(UIView *sv in subviews)
[self walkViews:sv];
}
-(void) scrollViewDidScroll:(UIScrollView *)scrollView
{
NSArray *windows = [UIApplication sharedApplication].windows;
for (UIWindow *w in windows)
{
[self walkViews:w];
}
}
Instead of having this code in a scroll event, you can put it wherever suits your needs. The main thing is that it walks all subviews looking for UIAutocorrectInlinePrompt. Instead of hiding it, you can do whatever is needed for your specific needs.

How to programmatically make text selectable and provide popup menus to go to another app

I am trying to implement a feature in iOS project that when you select a piece of text and highlight it you can then choose from the menu options to use another app like the default dictionary. Is it possible to do this? If so where can I find such documentation or tutorials?
You are describing the iOS menu. Look at the documentation on classes such as UIMenu, UIMenuItem, and UIMenuController.
I've found a solution to my problem.
Thanks to the author of this article:
http://blog.studiovillegas.com/2014/02/06/ios-uipasteboard-uimenucontroller-and-uimenuitem/
To add a custom menu item on to the default menu controller.
ViewController.h
- (void)longPressGestureRecognizer:(UIGestureRecognizer *)recognizer
{
UIMenuItem *mi = [self.label menuItemOpenPleco];
UIMenuController *menuController = [UIMenuController sharedMenuController];
menuController.menuItems = #[mi];
}
PasteboardLabel {h,m}
#interface PasteboardLabel : UILabel
- (UIMenuItem *)menuItemOpenPleco;
#end
#implementation PasteboardLabel
- (UIMenuItem *)menuItemOpenPleco
{
return [[UIMenuItem alloc] initWithTitle:#"Open Pleco" action:#selector(openPleco:)];
}
- (void)openPleco:(id)sender
{
NSString *selectedText = [self textInRange:[self selectedTextRange]];
UIPasteboard *pb = [UIPasteboard generalPasteboard];
pb.string = selectedText;
NSString *urlString = [NSString stringWithFormat:#"plecoapi://x-callback-url/q?s=%#", pb.string];
NSURL *url = [[NSURL alloc] initWithString:urlString];
[[UIApplication sharedApplication] openURL:url];
}
#end
I've found that there's a dearth of examples of adding custom menu items, or explanations of how they work. So I wanted to resolve that by sharing a few important tidbits then showing an example.
The UIMenuController "talks" with UIViews, not with UIViewControllers. This means that your UIMenuController related code needs to go into subclasses of UIView rather than a UIViewController.
Notice the word The at the start of my prior example. There's only one UIMenuController, a singleton which is shared from when your application first starts until it ends. This means that you should only add your item once, and that you shouldn't be writing over the existing array of items.
The appearance of the button in the UIMenu is based on whether or not the UIView that was tapped responds to the selector. This means you need to implement the method if you want the button to appear, and that you don't need to worry about it appearing when unrelated views are tapped unless you pick a selector name for which other UIViews also have methods.
So, having said all that, I made a subclass of a UITextView (which means its a subclass of UIView per my first bullet) and then I gave it this initialize method, along with an implementation for my selector.
+ (void)initialize {
static dispatch_once_t addInsert;
dispatch_once(&addInsert, ^{
UIMenuController *mController = [UIMenuController sharedMenuController];
UIMenuItem *insert = [[UIMenuItem alloc] initWithTitle:#"Insert..."
action:#selector(insert:)];
mController.menuItems = [mController.menuItems arrayByAddingObject:insert];
});
}
- (void)insert:(id)sender {
NSLog(#"Insert... pressed!");
}
The important points above here:
It's in the class initialize method, which is called by the runtime before the first time any other method in your class is invoked. In practice means the code is handled just before the first time an instance of your custom view will be appearing on screen.
I added a dispatch_once guard around it. If my class is subclassed, it's possible that those subclasses will call this initialize method. Maybe those subclasses show up before this one does, so I don't want to prevent the initialize method from running then. I just want to prevent it from running multiple times. Thus why I wrapped the code in a dispatch_once.
I didn't just set the menuItems to a new array of items - I assigned it to a new array of items that extended the existing array of items with my new item.
Hope you find all of that helpful. It's not very complicated, and you can certainly go about implementing my second point in other ways - I tried to pick a way that seemed safest to me, but there are certainly simpler ways of doing it.

Printing Contents of Multiple UIWebViews in one go

I have an iPad application that uses WebViews to display a list of URL's. I would like to be able to print all of the WebViews in one go, without prompting the user multiple times with the PrintInteractionController. The problem is that the PrintInteractionController does not appear to have the ability to do this. You cannot assign multiple viewFormatters, and the WebViews are not recognized as printItems. There is also no method that I can find to just print the items and not show the PrintInteractionController.
Does anyone know of a way to do this?
Cheers.
This is an old question, but I spent several days searching for an answer and following some misleading comments in the Apple docs and sample code. So, I'm sticking this here to save the next guy the days I wasted.
The specific problem is: How does one write a UIPrintPageRenderer that can print multiple UIWebViews in a single print job given to a UIPrintInteractionController?
You can get pretty far along to a solution that doesn't involve converting everything to PDFs first using this Apple sample code: PrintWebView. Also this documentation will help some: UIPrintPageRenderer. The problem is that the sample App and the documentation for UPPrintPageRenderer suggest that if you add multiple UIPrintFormatter via this property:
#property(nonatomic,copy) NSArray <UIPrintFormatter *> *printFormatters
The sample code Apple provided for the method to override -(NSInteger)numberOfPages in PrintWebView will just work and figure out the correct page counts.... Nope!
What you have to do is add the printFormatters in a non-obvious way via the method shown below, and then correct their respective startPage properties and use these collectively to work out the correct pageCount for UIPrintPageRenderer to return. Instead, use this:
-(void)addPrintFormatter:(UIPrintFormatter *)formatter startingAtPageAtIndex:(NSInteger)pageIndex
Why does this works and the other doesn't? No idea, probably a bug.... I should file a radar :) But Thanks to #Mustafa and this other Stackoverflow answer for the hint:
Here are the steps to follow to print multiple UIWebViews in one print job:
Follow the Apple outline in the PrintWebView example referenced above and write your UIPrintPageRenderer to set the properties you want on the webView.viewPrintFormatters you give to it.
Add the printFormatters via: [addPrintFormatter:startingAtPageAtIndex:]
In your UIPrintPageRenderer -(NSInteger)numberOfPages method get the pageCount from each formatter, and use that to update the startPage and total page count values.
Here is the basic code outline to follow (FYI: this solution is working for an App built with a deployment target of iOS 9.0):
Define your UIPrintPageRenderer class like so (you don't really need the init method, but in my use case I set values in there I always want that way):
#interface MyPrintPageRenderer : UIPrintPageRenderer
#end
#implementation MyPrintPageRenderer
-(id)init
{
self = [super init];
if(self) {
self.headerHeight = 0;
self.footerHeight = 0;
}
return self;
}
//
// Set whatever header, footer and insets you want. It is
// important to set these values to something, so that the print
// formatters can figure out their own pageCounts for whatever
// they contain. Look at the Apple sample App for PrintWebView for
// for more details about these values.
//
-(NSInteger)numberOfPages
{
__block NSUInteger startPage = 0;
for(UIPrintFormatter *pf in self.printFormatters) {
pf.contentInsets = UIEdgeInsetsZero;
pf.maximumContentWidth = self.printableRect.size.width;
pf.maximumContentHeight = self.printableRect.size.height;
dispatch_async(dispatch_get_main_queue(), ^{
pf.startPage = startPage;
startPage = pf.startPage + pf.pageCount;
});
}
return [super numberOfPages];
}
#end
In the Controller that handles the print action for the UIWebViews you want to print, do something like this:
-(void)printWebViews
{
MyPrintPageRenderer *pr = [MyPrintPageRenderer new];
//
// Add the printFormatters at sequential startingPageIndexes,
// your renderer will set them to the correct values later based
// on the various page metrics and their content.
//
[pr addPrintFormatter:_webView1.viewPrintFormatter startingAtPageAtIndex:0];
[pr addPrintFormatter:_webView2.viewPrintFormatter startingAtPageAtIndex:1];
[pr addPrintFormatter:_webView3.viewPrintFormatter startingAtPageAtIndex:2];
// Set whatever values needed for these as per Apple docs
UIPrintInfo *pi = [UIPrintInfo printInfo];
pi.outputType = UIPrintInfoOutputGeneral;
UIPrintInteractionController *pic = [UIPrintInteractionController sharedPrintController];
pic.printInfo = pi;
pic.printPageRenderer = pr;
[pic presentAnimated:NO completionHandler:nil];
}
And voila! Now... it just works ;)
Use printPageRenderer property of your UIPrintIterationController object. You can set multiple UIPrintFormatter subclasses in a UIPrintPageRenderer subclass object.

Resources