How to fade in/out navigationBar on iOS 9? - ios

Built-in Photo application fades in/out navigationBar when you tap on an image . This way Photo app allows to see it full screen.
How does it do this (fade efect)?
As I understand navigationController?.navigationBar.alpha doesn't work anymore (so you can't animate it this way).

Sharing all my finding.
Complain mode on
Frankly, I feel half pissed/like a dummy that I had to fight a good day to implement simple thing existing in Apple app.
Complain mode off
First of all here is some context. I am working with navigationBar which are provided by navigationController (vs just standalone bars which are manually dropped in your view)
There are several approaches which I found. I will mention all of them (even if I had no success using them)
1) Animate change of alpha of navigationBar
UIView.animateWithDuration(0.1, animations: {
navigationController?.navigationBar.alpha = 0
}, completion: nil)
#rmaddy mention here that it works for him. However, I believe he has a standalone bar (vs a bar managed by navigationController).
I used a tool Reveal to check UI hierarchy and found couple of things.
- There is a navigationBar which is hidden (and navigationController?.navigationBar is referencing it). So you can change alpha to your hearts joy, but these changes won't be visible.
There is however another navigationBar . I assume it's referenced in some private members of navigationController (let's call it private navigationBar). It's visible and that's what is displayed at the top of your view.
2) Use setNavigationBarHidden:animated:
This is a standard way to hide/show navigation bar. It's animated different way (it slides/up and down). However, if it's ok for you, just go with this is, because it's simple and clean.
navigationController?.setNavigationBarHidden(true, animated: true)
Additionally you can wrap it in UIView.beginAnimations, UIView.commitAnimations to animate it together with some other stuff (to make it smoother)
3) Animate change of alpha of private navigation bar.
This worked for me:
let privateNavigationBar = self.superview?.superview?.superview?.superview?.superview?.superview?.subviews[1]
UIView.animateWithDuration(0.1, animations: {
privateNavigationBar.alpha = 0
}, completion: nil)
I am going way up through the hierarchy to get a view which contains private navigationBar (which is second subview for that view).
However, this approach has multiple downsides:
I believe # of superviews? depends on your app hierarchy (vs you are using split view and so on). I think you can generalize or may be you just walk the whole hierarchy to find non hidden UINavigationBar to solve this.
I have a feeling that Apple may frown at this (your app be not accepted to AppStore)
4) Make navigationBar transparent and set background image to be transparent and change alpha channel on it.
I can't find where I read about this idea. There was couple of mentioning.
There is Apple example app which shows how to customize NavigationBar, including making it transparent.
It's interesting that this example app works for me (the navigation bar is transparent in it). However, when I tried this code in my app it didn't work (I still didn't figured out what is going on with this). As usual there are bunch of variables (may be something in Info.plist, also they subclass NavigationController, also may be something in view hierarchy)
5) Adding standalone navigationBar
You can hide a bar provided by navigationController. Add your own to the UIView, wire it to #IBOutlet and use alpha animation on it (most likely that's what #rmaddy was referring too).
I checked and this is work.
This approach is used in this tutorial.
However, it has a downside:
I believe it won't handle well rotation, increase of statusbar height while call or GPS
Each time when I see a code like this (written in the article) I know that there will be problems with resizing: CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), 64.0)
You can potentially replace it with constrains. I went this route, but stumble upon some issues.
6) Other methods
I saw two more methods. I don't know whether they will work or what will be downsides:
One of them in this question: How to hide/show status bar and navigation bar by fading in/out at the same time like the Photos app in iOS 7?
And this answer: https://stackoverflow.com/a/18063898/422080

Related

"Print" & "Cancel" disappeared from Printer Options in UIActivityViewController

The buttons are still there and function fine, however as you can see they are completely invisible. Adjusting font colors and navigation bar colors had no effect. I have been trying to resolve this for months and despite reading Apple's documentation, I am unable to even see where you would change what I assumed to be a system wide function.
So to help narrow down this issue I created a simple navigation controller with a print button. If I put these both on the Login storyboard (the first storyboard) it works perfectly fine. If I put it on Main.storyboard (the second storyboard) it doesn't work.
If I change my project settings to make Main.storyboard the first storyboard it works. Leading me to believe the issue is in my FirstViewController.
You can set the color of the print and cancel button
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UIToolbar.self]).tintColor = UIColor.green
After narrowing down the problem to the first view controller I discovered the tint was set to Clear Color (ie Alpha is 0). Fixing this resolved the problem in the rest of the app. I am surprised a setting in a view can propogate to the rest of the app and overwrite all of the other views in the process. Perhaps apple set the first view to determine the colors for system pop ups etc.

Embedding Share Icons Inline Within Another View

I am attempting to implement a "Share" feature in my iOS app with a similar look and feel to that of the Google Photos iOS app:
The bottom two rows of icons are what I care about. They look nearly identical to those displayed when using the UIActivityViewController.
In the Google Photos app, these icons appear inline with the "select photos" portion of the screen (e.g. you can still interact with the upper portion of the screen). However, the documentation for the UIActivityViewController states that "On iPhone and iPod touch, you must present [the view controller] modally."
This is the difference that is really important to me -- I'd like for the "share" icons to display inline with the rest of my content, rather than having a modal that is displayed on top of my content.
Is it possible to use the UIActivityViewController to achieve a similar effect shown in the screenshot above? If not, is there a recommended approach that I might use to implement this sort of functionality?
As discussed in another answer, reverse engineering UIActivityViewController is the only option in order to be able to achieve a similar effect. I tried this using iPhone 6s - 10.3 Simulator. The following findings may not be accurate for iOS 9.x or 11.x or above.
A. Find out all internal variables for UIActivityViewController
var variablesCount: UInt32 = 0
let variables = class_copyIvarList(UIActivityViewController.self, &variablesCount)
for i in 0..<variablesCount {
if let variable = variables?[Int(i)] {
let name = String(cString: ivar_getName(variable))
let typeEncoding = String(cString: ivar_getTypeEncoding(variable))
print("\(name)\n\(typeEncoding)\n\n")
}
}
free(variables)
The ones those got my attention at first sight are (in order) -
_activityViewController
#"UIViewController"
_contentController
#"_UIActivityViewControllerContentController"
_activityAlertController
#"UIAlertController"
On inspecting them further, I found out that _contentController is the one we should be looking for. We need to look one level deeper in hierarchy for UICollectionViewController to get to where we want to be.
if let activityContentController = activityVC.value(forKeyPath: "_contentController") as? UIViewController {
print("Found _contentController!")
for child in activityContentController.childViewControllers {
print(String(describing: child))
if child is UICollectionViewController {
print("Found UICollectionViewController!")
break
}
}
}
Why did I look for UICollectionViewController?
Debug View Hierarchy has the answer for this.
I tried adding this as a childViewController to my UIViewController -
self.addChildViewController(child)
child.didMove(toParentViewController: self)
self.view.addSubview(child.view)
child.view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
child.view.topAnchor.constraint(equalTo: self.view.topAnchor),
child.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
child.view.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
child.view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
])
IT SHOWS UP CORRECTLY ONLY IF YOU HAVE LOADED/PRESENTED UIActivityViewController FIRST.
I was able to achieve this using a silent present/dismiss call -
self.present(activityVC, animated: false, completion: {
self.dismiss(animated: false, completion: nil)
})
IS THIS APP STORE SAFE? - Most likely not.
As soon as you start stealing the view(s) or viewController(s) from UIKit's standard components, the behavior is not stable and it will break with upcoming updates for sure.
What Google Photos has is the result of way more advanced reverse engineering. In above implementation, you can't see More option screen. The hierarchy UIActivityViewController expects is broken.
Hope this helps.
Okay, I thought about this and I did some intensive research on the web but nobody ever seemed to needed to modify it like you want. So here are my guesses how Google engineers solved this:
They reverse engineered the UIActivityViewController and call some private APIs to get the same icons to show up and the reordering controllers
They use the UIViewController transitioning API and hack the view hierarchy of a modally presented UIActivityViewController, removing the cancel button and adding some custom views on top of its view
An indicator for the second option could be that the top of the presented "sheet" has a white background while the bottom has the greyish color.
Unfortunately I'm not very fit with the transitioning API as I'm just about to learn it but my understanding is that you can provide a custom object as the transitioning delegate.
This object then gets called when you present/dismiss or push/pop a UIViewController. It will get a reference to both, the presenting and the presented view controller and you can have fun with both of their views.
This should make it "quiet" easy to remove and add some subviews, change frames and colors etc while still having all the default behavior.
I hope this answer helps you to achieve what you want. However, be aware that the structure of the controller could change at any time so always make sure to also test agains betas so that you don't get caught by surprise when apple releases an update which breaks your UI. :)

How to show double height green statusbar (In-Call) in foreground app on device?

There's a lot of questions here asking for displaying a red recording bar while in background. It's totally clear I should use AVAudioSession category AVAudioSessionCategoryPlayAndRecord for that. My question is how can I display a green In-Call bar (or at least red bar) in a foreground app when having an active VOIP call in my app? So I could return to call UI tapping a statusbar area, just like Whatsapp or Skype does.
What I've already tried:
voip and audio modes in UIBackgroundModes key in Info.plist + setCategory:AVAudioSessionCategoryPlayAndRecord + setActive as suggested in this SO answer (gives me a red statusbar when going background, but nothing while in foreground)
Previous + AVAudioSession + setMode:AVAudioSessionModeVoiceChat - didn't work
Set kCFStreamNetworkServiceTypeVoIP flag to socket in pjsip sources and recompiled it - didn't help. Also, deprecated since iOS 8.
Created a separate socket, set the voip flag for inputStream/outputStream: [self.inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] (took the sample code from here)
Using pjsip for calls. What else can I try to increase a statusbar height, moving all the UI down?
Are there any standard ways to do that, or I should hack it my own by resizing a root UIWindow and setting another green UIWindow under the statusbar?
Edit:
Since no answer is found for a standard way to do that, accepted #roman-ermolov answer. For those who will search for an answer I may suggest several options to do it yourself:
Wrap your root viewcontroller inside container, like in Apple's iAdSuite with Storyboards example. Take a look at my sample project for example. Probably the best way to make that bar.
Hack UINavigationBar height (see this approach) - doesn't work for landscape yet, but may probably be solved
Control your main UIWindow's frame yourself, place another UIWindow under the statusbar with the desired content.
I have made an investigation of both apps (WhatsApp & Skype) and learned that they used their own UIView for achieving this functionality.
This is Skype:
This is WhatsApp (Reveal can't get real snapshot, but it transmits basic idea):
In both apps it is a UIView which starts on top of the screen and several methods for format output text / handle touch on it.
In iOS 10 Apple introduced CallKit.framework, but it seems that it has not that kind of functionality too, so the only way to do it - do it yourself.
Roman Ermolov's answer explained that some apps use their own UIView for this functionality, but does not explain how. The way I see it, you have a few options.
If you want to lay the view on top of (and covering) the current view controller, you could create a UIView with a UILabel or your own UIWindow and add that as a subview of your applications' keyWindow.
To have the view stick on top without covering the contents of the view controller, you could take the same approach as above and manually resize the application window or window's root view controller when you add the call bar subview. Alternatively, you could create a UIViewController base class which contains this call bar subview and an outlet for the view's height constraint. The default value of this constraint should be zero. Then, when a call is placed, change the height constraint to your desired call back height. This constraint approach might work on the application's UIWindow as well, but I'm not too familiar with using constraints on an application's window.

Full customisation of UINavigationBar

I'm currently building an iOS Application for a client and have hit a pretty huge roadblock. I mean, I could write my own UINavigationBar and such but that would cause a lot of issues further down the road.
I have tried everything in my knowledge so far and have spent several hours searching for a solution (overriding the CALayer, using CoreGraphics and pretty much everything else ) and I get the same result. No matter how hard I try to remove the background of the UINavigationBar, it still shows a white background with slight translucency.
I need to have a lot of customisation on the navigation bar (I.E having a gradient going from "blackColor" to "clearColor" and I can't do that if the background of the Navigation Bar refuses to be completely transparent. I have tried copying all of the CALayers from the UINavigationBar layer to a subview I added and it just kept crashing, even when replacing the delegates and superlayer.
I really need help with this. One of the multiple effects I'm trying to achieve are below. (The blue rectangle is not the focus of the image, it's irrelevant.)
To get it completely transparent:
(UINavigationBar.appearance()).translucent = true
(UINavigationBar.appearance()).barTintColor = UIColor.clearColor()
(UINavigationBar.appearance()).backgroundColor = UIColor.clearColor()
(UINavigationBar.appearance()).setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
(UINavigationBar.appearance()).shadowImage = UIImage()
EDIT:
Using iOS 8.3 looking like this:
Or am I missing something?
You are referring to the top Navbar with the back button and Save button right? I'm just confused as to what the blue box on the image is in reference too.
Anyways, this is an extremely hacky approach but could work:
Set the UINavigationBar's alpha to 0. The Back button and Save button will probably also disappear but you could just add labels to the View Controller at the top in the exact same place. The button's should still be functional even though they are "invisible" but the user will still think they are touching them.
Again, very hacky, but you are free to play with the top part of the View then. I'm sure there's a better way to do this but I'm not near a computer with XCode at the moment and can't test it out, and this fix may not even work but I thought I'd offer up a possible temporary solution.

iOS: Add an image to the Navigation bar

I'm currently working on an iOS app that is pretty much supposed look and work like an already existing android app.
As a part of that, I'd like to make the top navigation look the same, but since iOs devices do not provide a hardware or software integrated back button, I'd also like to keep the back button navigation that's common to iOs apps.
I've tried numerous attempts to achieve it, but I'll stick with the latest I'm working on since it looks the most promising. I've started to work on a custom UINavigationBar class that overrides the initWithCoder: and layoutSubviews: methods. The result is as follows:
As you can see, the back button now overlaps the application icon. What I'm looking for is a way to make the button and the text to scale into the space right of the application icon. I've tried to handle this in layoutSubviews: but the superclass logic appears to be rather complex, I was not able to reproduce it's functionality (in particular it seems to be working with some private variables I don't have access to).
I also tried manipulating self.frame before calling [super layoutSubviews] but apart from endless loops I was not able to achieve anything in that direction.
I'd be happy if anyone could point me into the right direction.
If I understand your problem correctly, I think you could do this by adding a custom titleView to the UINavigationItem, and have an image view (on the left side for the icon) and a label (for the title) as subviews. This would replace the default title.

Resources