Hide "Show More" button from Today widget in iOS10 - ios

I am building an iOS Today widget, and while testing for iOS 10, I see a "Show More" / "Show Less" button on the top right of the widget header. How can I remove this button? I am using Objective-C.

In iOS 10, as far as I know, the show more option is new and we cannot remove it, but we can modify it as needed.
The following code will allow you to automatically size the Today widget. Just change the table or collection view or whatever you used in your project.
static CGFloat padding = 25.0;
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
// This will remove extra separators from tableview
self.articleTableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
// Add the iOS 10 Show More ability
[self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded];
}
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeCompact){
// Changed to compact mode
self.preferredContentSize = maxSize;
}
else{
// Changed to expanded mode
self.preferredContentSize = CGSizeMake(self.articleTableView.contentSize.width, self.articleTableView.contentSize.height + padding);
}
}

In viewDidLoad you can set the largest available display mode.
[self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeCompact];
This will remove the Show More/Less button, but it may not be what you want. The maximum allowed size for the compact view is fairly small.
You can implement:
-(void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode withMaximumSize:(CGSize)maxSize
to update your preferredContentSize. The maxSize parameter will be the maximum allowed size for the activeDisplayMode.

-[NCWidgetProviding widgetActiveDisplayModeDidChange:withMaximumSize:]
Is probably what you're looking for, I would reference this

Sadly you cannot hide it and should conform to the
widgetActiveDisplayModeDidChange:withMaximumSize:
widgets that doesn't show this control were not build for iOS10

I know the original post mentions using objective-c
but in the event anyone needs the swift answer, here it is
override func viewDidLoad()
{
super.viewDidLoad()
self.extensionContext?.widgetLargestAvailableDisplayMode = .compact
}
When set to compact, the app will only support compact mode i.e. show less/show show buttons/functionality will be gone.
here's some documentation for more info

Placing this line of code inside the widgetActiveDisplayModeDidChange delegate method solved my problem.
[self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded];
If you wanna hide the show more/ show less option replace NCWidgetDisplayModeExpanded with NCWidgetDisplayModeCompact.
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode
withMaximumSize:(CGSize)maxSize {
[self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded];
}

Related

iOS Facebook Like Button Not Resizing

I am setting up an iOS app and have implemented a Facebook like button. Problem is, when I try to adjust the size according to their documentation found here, it just does not resize. Here's my setup code which is all the code that uses the like button:
(ViewController.h)
#property FBSDKLikeControl* likeButton;
(ViewController.m)
#synthesize likeButton;
- (void)viewDidLoad {
...
likeButton = [[FBSDKLikeControl alloc] initWithFrame:CGRectMake(20, 20, 128, 64)];
likeButton.objectID = #"https://www.facebook.com/mypage";
}
- (void)showLikeButton {
[self.view addSubview:likeButton];
}
- (void)hideLikeButton {
[likeButton removeFromSuperview];
}
When showLikeButton is called (which is after the app has been running a little while), the button shows up as expected, but no matter what size I initialize the button with, it doesn't change its size. Any ideas? Thanks in advance.
Use FBSDKLikeButton in place of FBSDKLikeControl. It inherits from UIButton and responds to sizing changes, although the image resolution suffers.

Get size of currently visible UIViewController from a UIView

I've been looking at the same problem for so long I'm probably missing a simple solution here.
I created a small library to provide a custom UIView that sticks to the keyboard like the one for iMessage does (aka doesn't hide with keyboard): https://github.com/oseparovic/MessageComposerView
Basically the problem I'm experiencing is that when the user init's custom view I want a view with the following default rect initialized:
CGFloat defaultHeight = 44.0;
CGRect frame = CGRectMake(0,
[self currentScreenSize].height-defaultHeight,
[self currentScreenSize].width,
defaultHeight)
This requires that the currentScreenSize is calculated within the UIView. I've tried multiple implementations all of which have their downsides. There doesn't seems to be a good solution due to this breaking principles of MVC.
There are lots of duplicate questions on SO but most assume you have access to the rest of the code base (e.g. the app delegate) which this custom view does not so I'm looking for a self contained solution.
Here are the two leading implementations I'm using:
NextResponder
This solution seems to be fairly successful in a wide variety of scenarios. All it does is get the next responder's frame which very conveniently doesn't include the nav or status bar and can be used to position the UIView at the bottom of the screen.
The main problem is that self.nextResponder within the UIView is nil at the point of initialization, meaning it can't be used (at least not that I know) to set up the initial frame. Once the view has been initialized and added as a subview though this seems to work like a charm for various repositioning uses.
- (CGSize)currentScreenSize {
// return the screen size with respect to the orientation
return ((UIView*)self.nextResponder).frame.size;
}
ApplicationFrame
This was the solution I was using for a long time but it's far more bulky and has several problems. First of all, by using the applicationFrame you have to deal with the nav bar height as it will otherwise offset the position of your view. This means you have to determine if it is visible, get its height and subtract it from your currentSize.
Getting the nav bar unfortunately means you need to access the UINavigationController which is not nearly as simple as accessing the UIViewController. The best solution I've had so far is the below included currentNavigationBarHeight. I recently found an issue though where this will fail to get the nav bar height if a UIAlertView is present as [UIApplication sharedApplication].keyWindow.rootViewController will evaluate to _UIAlertShimPresentingViewController
- (CGSize)currentScreenSize {
// there are a few problems with this implementation. Namely nav bar height
// especially was unreliable. For example when UIAlertView height was present
// we couldn't properly determine the nav bar height. The above method appears to be
// working more consistently. If it doesn't work for you try this method below instead.
return [self currentScreenSizeInInterfaceOrientation:[self currentInterfaceOrientation]];
}
- (CGSize)currentScreenSizeInInterfaceOrientation:(UIInterfaceOrientation)orientation {
// http://stackoverflow.com/a/7905540/740474
// get the size of the application frame (screensize - status bar height)
CGSize size = [UIScreen mainScreen].applicationFrame.size;
// if the orientation at this point is landscape but it hasn't fully rotated yet use landscape size instead.
// handling differs between iOS 7 && 8 so need to check if size is properly configured or not. On
// iOS 7 height will still be greater than width in landscape without this call but on iOS 8
// it won't
if (UIInterfaceOrientationIsLandscape(orientation) && size.height > size.width) {
size = CGSizeMake(size.height, size.width);
}
// subtract the height of the navigation bar from the screen height
size.height -= [self currentNavigationBarHeight];
return size;
}
- (UIInterfaceOrientation)currentInterfaceOrientation {
// Returns the orientation of the Interface NOT the Device. The two do not happen in exact unison so
// this point is important.
return [UIApplication sharedApplication].statusBarOrientation;
}
- (CGFloat)currentNavigationBarHeight {
// TODO this will fail to get the correct height when a UIAlertView is present
id nav = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([nav isKindOfClass:[UINavigationController class]]) {
UINavigationController *navc = (UINavigationController *) nav;
if(navc.navigationBarHidden) {
return 0;
} else {
return navc.navigationBar.frame.size.height;
}
}
return 0;
}
Does anyone have suggestion about how I can best calculate the UIViewController size from within this UIView. I'm totally open to other suggestions on how to stick the UIView to the bottom of the screen upon initialization that I may have overlooked. Thank you!
+ (id) getCurrentUIViewController : (id)res {
if([res isKindOfClass:[UIViewController class]]) {
return res;
}
else if ([res isKindOfClass:[UIView class]]) {
return [Function getCurrentUIViewController:[res nextResponder]];
}
else {
return nil;
}
}

Disable horizontal scrolling in UITextView

Hello: I'm building an app that supports iOS 6 and higher. I have a few UITextViews throughout the app, and I noticed that on iOS 6, the text views are able to be scrolled horizontally. On iOS 7, they can only be scrolled vertically. Is there a way to restrict scrolling so that it will only scroll vertically?
I've checked out some other similar questions, but I don't want to add a UILabel to a UIScrollView.
Any help is much appreciated!
EDIT
When using the following two lines (per the answers suggested), this still doesn't work when setting content insets. Anyone know how to fix this?
Attempt to disable scroll:
tView.contentSize = CGSizeMake(tView.frame.size.width, tView.contentSize.height);
tView.showsHorizontalScrollIndicator = FALSE;
Insets:
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
tView.contentInset = UIEdgeInsetsMake(kTextViewInsets, kTextViewInsets, kTextViewInsets, kTextViewInsets);
} else {
tView.textContainerInset = UIEdgeInsetsMake(kTextViewInsets, kTextViewInsets, kTextViewInsets, kTextViewInsets);
}
I would subclass UITextView and override setContentOffset:
- (void)setContentOffset:(CGPoint)contentOffset
{
super.contentOffset = CGPointMake(0.0, // Ignore the passed offset. Could also use self.contentOffset.x
contentOffset.y);
}
Try this -
mytextView.contentSize = CGSizeMake(mytextView.frame.size.width,HEIGHT_YOU_WANT);
mytextView.showsHorizontalScrollIndicator = NO;
ALso take a look at SO Question may it help you.

How to set the height of a Today Widget Extension?

How can i change the height of my App's Today Extension in the Notification Center?
I tried it with the Interface Builder and with Code,
the Interface Builder Displays the View with height 600, but it's not applying this height on the device.
It seems I can't get it bigger than some 80 pixels...
In your widget UIViewController.m (Objective-C):
self.preferredContentSize = CGSizeMake(0, 200);
Will make your widget have a height of 200.
Note that the width will have no affect on the view, as widgets must fit in the exact width of notification center, which is handled automagically.
Also, if you want to animate changes in the height of your view, you can implement (Objective-C):
- (void)viewWillTransitionToSize:(CGSize)size
withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
in your view controller using -animateAlongsideTransition:completion:
The answer was a bit hidden; you had to click around in the documentation sidebar to eventually find this fantastic document.
Another way is to use auto-layout constraints to constrain your view's height.
Widgets have their heights adjusted by the system. If you have defined your height using constraints this will be automatically adjusted as required. If you're using explicit layout you can request a new height by modifying the preferredContentSize of your widget.
Note that you have no guarantee notification center will respect your height request: it may be adjusted automatically, it may be adjusted but not to the exact height you want, or it may not be honored at all.
The best way to develop a widget is to use auto-layout constraints to set your height values, that way your widget will adapt to different heights with ease.
Since iOS 10 extension's height is 110 pixels. You should use new protocol method widgetActiveDisplayModeDidChange:withMaximumSize: to extend extension size (Objective-C):
- (void)widgetActiveDisplayModeDidChange:(NCWidgetDisplayMode)activeDisplayMode
withMaximumSize:(CGSize)maxSize {
if (activeDisplayMode == NCWidgetDisplayModeExpanded) {
self.preferredContentSize = CGSizeMake(maxSize.width, 600.0);
} else if (activeDisplayMode == NCWidgetDisplayModeCompact) {
self.preferredContentSize = maxSize;
}
}
Also you may need to call setWidgetLargestAvailableDisplayMode: on your extension context in today view controller's viewDidLoad method like this (Objective-C):
if ([self.extensionContext respondsToSelector:#selector(setWidgetLargestAvailableDisplayMode:)]) { // iOS 10+
[self.extensionContext setWidgetLargestAvailableDisplayMode:NCWidgetDisplayModeExpanded];
} else {
self.preferredContentSize = CGSizeMake(0, 600.0); // iOS 10-
}
This thread may be helpful https://forums.developer.apple.com/thread/48930
The best way is of course Autolayout but by default there are margins which you can control like this
func widgetMarginInsetsForProposedMarginInsets
(defaultMarginInsets: UIEdgeInsets) -> (UIEdgeInsets) {
return UIEdgeInsetsZero
}
There are two ways to display Today extension:
Compact Mode (fixed height for Widget)
Expand Mode (Variable height for Widget)
Whatever code you do to change the height of extension in Compact mode will not make any difference. So you need to change the mode from compact to Expand Mode.
// 1. Load This in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.extensionContext?.widgetLargestAvailableDisplayMode = NCWidgetDisplayMode.expanded
}
// 2. Implement another widget protocol
func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize){
if (activeDisplayMode == NCWidgetDisplayMode.compact) {
self.preferredContentSize = maxSize;
}
else {
self.preferredContentSize = CGSize(width: 0, height: 200);
}
}
You can refer WWDC for more updates about App extensions
Today widget default UIEdgeInsets defaultMarginInsets
(UIEdgeInsets) defaultMarginInsets = (top = 0, left = 44, bottom = 39, right = 0)
You should add this method
- (UIEdgeInsets)widgetMarginInsetsForProposedMarginInsets:(UIEdgeInsets)defaultMarginInsets {
UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0, 44, 0, 0);
return edgeInsets;}

Animated Resize of UIToolbar Causes Background to be Clipped on iOS <5.1

I have implemented a custom split view controller which — in principle — works quite well.
There is, however one aspect that does not work was expected and that is the resize-animation of the toolbar on iOS prior to version 5.1 — if present:
After subclassing UIToolbar to override its layoutSubviews method, animating changes to the width of my main-content area causes the toolbar-items to move as expected. The background of the toolbar — however — does not animate as expected.
Instead, its width changes to the new value immediately, causing the background to be shown while increasing the width.
Here are what I deem the relevant parts of the code I use — all pretty standard stuff, as little magic/hackery as possible:
// From the implementation of my Split Layout View Class:
- (void)setAuxiliaryViewHidden:(BOOL)hide animated:(BOOL)animated completion:(void (^)(BOOL isFinished))completion
{
auxiliaryViewHidden_ = hide;
if (!animated)
{
[self layoutSubviews];
if (completion)
completion(YES);
return;
}
// I've tried it with and without UIViewAnimationOptionsLayoutSubviews -- didn't change anything...
UIViewAnimationOptions easedRelayoutStartingFromCurrentState = UIViewAnimationOptionCurveEaseOut | UIViewAnimationOptionBeginFromCurrentState;
[UIView animateWithDuration:M_1_PI delay:0.0 options:easedRelayoutStartingFromCurrentState animations:^{
[self layoutSubviews];
} completion:completion];
}
- (void)layoutSubviews
{
[super layoutSubviews];
// tedious layout work to calculate the frames for the main- and auxiliary-content views
self.mainContentView.frame = mainContentFrame; // <= This currently has the toolbar, but...
self.auxiliaryContentView.frame = auxiliaryContentFrame; // ...this one could contain one, as well.
}
// The complete implementation of my UIToolbar class:
#implementation AnimatableToolbar
static CGFloat sThresholdSelectorMargin = 30.;
- (void)layoutSubviews
{
[super layoutSubviews];
// walk the subviews looking for the views that represent toolbar items
for (UIView *subview in self.subviews)
{
NSString *className = NSStringFromClass([subview class]);
if (![className hasPrefix:#"UIToolbar"]) // not a toolbar item view
continue;
if (![subview isKindOfClass:[UIControl class]]) // some other private class we don't want to f**k around with…
continue;
CGRect frame = [subview frame];
BOOL isLeftmostItem = frame.origin.x <= sThresholdSelectorMargin;
if (isLeftmostItem)
{
subview.autoresizingMask = UIViewAutoresizingFlexibleRightMargin;
continue;
}
BOOL isRightmostItem = (CGRectGetMaxX(self.bounds) - CGRectGetMaxX(frame)) <= sThresholdSelectorMargin;
if (!isRightmostItem)
{
subview.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin;
continue;
}
subview.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
}
}
#end
I’ve set the class of the toolbar in InterfaceBuilder and I know for a fact, that this code gets called and, like I said, on iOS 5.1 everything works just fine.
I have to support iOS starting version 4.2, though…
Any help/hints as to what I’m missing are greatly appreciated.
As far as I can see, your approach can only work on iOS SDK > 5. Indeed, iOS SDK 5 introduced the possibility of manipulating the UIToolbar background in an explicit way (see setBackgroundImage:forToolbarPosition:barMetrics and relative getter method).
In iOS SDK 4, an UIToolbar object has no _UIToolbarBackground subview, so you cannot move it around in your layoutSubviews implementation. To verify this, add a trace like this:
for (UIView *subview in self.subviews)
{
NSLog(#"FOUND SUBVIEW: %#", [subview description]);
run the code on both iOS 4 and 5 and you will see what I mean.
All in all, the solution to your problem lays in handling the background in two different ways under iOS 4 and iOS 5. Specifically, on iOS 4 you might give the following approach a try:
add a subview to your custom UIToolbar that acts as a background view:
[toolbar insertSubview:backgroundView atIndex:0];
set:
toolbar.backgroundColor = [UIColor clearColor];
so that the UIToolbar background color does not interfere;
in your layoutSubviews method animate around this background subview together with the others, like you are doing;
Of course, nothing prevents you from using this same background subview also for iOS 5, only thing you should beware is that at step 1, the subview should be inserted at index 1 (i.e, on top of the existing background).
Hope that this helps.
Since I think this is going to be useful for someone else, I’ll just drop my solution here for reference:
Per sergio’s suggestion, I inserted an additional UIImageView into the view hierarchy. But since I wanted this to work with the default toolbar styling, I needed to jump trough a few hoops:
The image needed to be dynamically generated whenever the tintColor changed.
On iOS 5.0.x the toolbar background is an additional view.
To resolve this I ended up…
Implementing +load to set a static BOOL on whether I need to do anything. (Parses -[UIDevice systemVersion] for version prior to 5.1).
Adding a (lazily loaded) property for the image view stretchableBackground. The view will be nilif my static flag is NO. Otherwise the view will be created having twice the width of [UIScreen mainScreen], offset to the left by half that width and resizable in height and right margin and inserted into the toolbar at index 0.
Overriding setTintColor:. Whenever this happens, I call through to super and __updateBackground.
Implemented a method __updateBackground that:
When the toolbar responds to backgroundImageForToolbarPosition:barMetrics: get the first subview that is not our stretchableBackground. Use the contents property of that view’s layer to populate the stretchableBackground’s image property and return.
If the toolbar doesn’t respond to that selector,
use CGBitmapContextCreate() to obtain a 32bit RGBA CGContextRef that is one pixel wide and as high as the toolbar multiplied by the screen’s scale. (Use kCGImageAlphaPremultipliedLast to work with the device RGB color space…)
Translate the CTM by that height and scale it by scale/-scale to transition from UIKit to CG-Coordinates and draw the view’s layer into that context. (If you fail to do this, your image will always be transparent blank…)
Create a UIImage from that context and set it as the stretchableBackground’s image.
Notice that this fix for iOS 5.0.x will not work as expected when using different background images for portrait and landscape or images that do not scale — although that can be tweaked by configuring the image view differently…

Resources