How to reproduce Apple's alert-like views - ios

I am struggling the last couple of days to figure out if these alerts are part of UIKit or just a private custom view. I don't even know how they properly called. The UI team have asked for an alert that looks like the one appears in Apple's News and Podcast apps. See screenshots below.

Apple's code to perform this is not part of UIKit. It will need to be implemented by you! I made a framework to do just this on GitHub called AOModalStatusView (https://github.com/alecdoconnor/AOModalStatusView).
The easiest way to do this would be with a custom view presented modally and with the presentation style set to "over current context" so that what is behind the view shows through. Inside the view create a square that is centered on the view. Give it rounded corners and a specific width and height. In order to get the blurred background you should use Apple's "Visual Effects View with Blur" or "Visual Effects View with Blur and Vibrancy." (https://shrikar.com/ios-development-tutorial-visual-blur-with-uivisualeffectview/)
In the view controller for this view, set a timer shortly in viewDidAppear(..) that will run dismiss(animated: true, completion: nil)

You can use my own StatusAlert framework written in Swift. It gives you ability to show Apple system-like alert as well as present the same alert without an image, title or message anywhere in UIView.
It is available via Cocoapods and Carthage and supports iPhone X, Safe Areas layout, iPads and allows some customizations.

Related

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 fade in/out navigationBar on iOS 9?

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

How to position a UIAlertController's view?

I'm writing an app in Swift, targeting iOS 8. Does anyone know whether it's possible to programmatically position the alerts that are displayed via UIAlertController? The documention makes no mention of position or location, so I tried setting the controller's view.frame property to a new CGRect, but alerts still appearing dead center.
The alert view, by design, shows right in the center. This is because the experience is intended to be familiar and simple for the end user. In the documentation for UIAlertController it also notes:
The view hierarchy for this class is private and must not be modified.
If you wish to manipulate the placing of the frame, you've got several options that use a plain old UIView/UIViewController. Here is one I've used where you can do what you want that's written in swift: https://github.com/vikmeup/SCLAlertView-Swift

iOS map style config slide up?

iOS 7 has changed how maps are displayed by apps on the iPhone, and especially how the user configures the map.
The map is displayed like this, with an Info button.
When the user taps the Info button, the configuration screen slides up.
How do I recreate a screen overlay like this in my code? While I don't want the user to be dropping pins, I want the user to be able to switch between different map styles or open the address I'm pointing to in Apple's Maps app.
If you are trying to do a simple overlay view, then look into making an UIView slide up through UIView animations. Other than that, you would need an UISegmentedControl and related code. You would also need to blur the view and make it translucent, so look into my other question and answer on how to properly blur an UIView:How to apply blur to a UIView?
I've never looked into it, but I've heard the only easy way to do a translucent background (as in, without writing your own OpenGL code) is to create a UIToolbar instance.
I'm guessing you'd make a toolbar with no items, just treat it like UIView and add your own subviews (UITableView, etc). It is a subclass of UIView.

How to create a see through button with "blurred" background in iOS 7

I am updating my app to iOS 7 and wanted to improve the UI. I want to create a see through background like Control Center. So basically I have a button, which I would like to have a see through and "blurred" background like control center. Thanks!
There's nothing to stop you using a UIToolbar.
Create a UIView as a base view with clear background, add a UIToolbar anchored to it's edges so it fills the view and then add your other controls to your base view above the toolbar.
It'll look for example like this:
The blurring effect is not available as a public API. There are third party implementations available, such as FXBlurView, which I can recommend.
You shouldn't really be using a toolbar. It's called toolbar for a reason.
One way to go would be to take a snapshot image of your background and blur it (you can use UIImage+ImageEffects category included in Apple samples to achieve consistent effect).
You can then just crop the image to desired frame and use it as a background of your button.
Are you looking to have a 'dynamic' blur effect (like in Control Center)? It's still doable, but it makes things more complicated.
You can also create blur effects with GUPImage Framework here is a full tutorial about it iOS blur effect tutorial. The tutorial also explains why you shouldn't use toolbar for blur effect creation

Resources