Reproducing Music App's NavigationBar Animation - ios

In my project I want to achieve a very similar animation like in the Artist-ViewController of the stock Music App.
When the UITableView scrolls to a specific offset the navigationBar's tintColor, the color of the title and the color of the statusbar changes.
To achieve that I added an UIView (I named it containerView) as a subView of the UINavigationBar and inside that containerView I added a UIBlurEffect. Now when the tableView scrolls I am listening to scrollViewDidScroll to change the earlier named attributes which works really good.
containerView.frame = CGRect(x: 0, y: -(statusBarHeight), width: UIScreen.main.bounds.width, height: frame.height + statusBarHeight)
containerView.clipsToBounds = true
insertSubview(containerView, at: 0)
let blurEffect = UIBlurEffect(style: .extraLight)
overlayView.effect = blurEffect
overlayView.backgroundColor = unOverlayColor
let height = frame.height + statusBarHeight
overlayView.frame = CGRect(x: 0, y: containerView.frame.height, width: containerView.frame.width, height: height)
containerView.addSubview(overlayView)
My only problem is that the containerView is placed above the UINavigationBar's navigationItems and therefore hides them.
So my question is how can I add the containerView behind the navigationItems to the UINavigationBar?
I figured out that it has to be something with the way how navigationBars are handled in iOS 11 because on iOS 10 everything works fine.

Related

iOS SearchController Searchbar does not work after adding constraint programmatically

I have an iOS project whereby the SearchController's SearchBar is added to the view as a subview:
let subView = UIView(frame: CGRect(x: 0, y: 65.0, width: 350.0, height: 45.0))
subView.addSubview((self.searchController.searchBar))
self.view.addSubview(subView)
As shown in the screenshot below, the searchbar is partially obscured by the navigation bar:
As such, I added the following constraint:
subView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([subView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor)])
The searchbar is now positioned correctly below the navigation bar as shown:
However, I'm now not able to click onto the searchbar to input any text. How can I resolve this?
How I resolved this at the end.
I still made use of a subView for the searchbar. But this time, instead of giving a fixed value for y, I used the heights of the safeArea and the navigation bar as shown:
let window = UIApplication.shared.windows.first
let safeheight = window?.safeAreaInsets.top
let navHeight = self.navigationController?.navigationBar.frame.height
let subView = UIView(frame: CGRect(x: 0, y: safeheight! + navHeight!, width: 350.0, height: 45.0))
subView.addSubview((self.searchController.searchBar))
self.view.addSubview(subView)
This time, the searchbar was positioned correctly and worked perfectly.

UI freezes for a second when returning to the previous ViewController in Navigation Stack

I'm having some trouble figuring out what's causing the UI to freeze for a second when I press the back button. It started happening after I added a background image to the viewController that I'm transitioning from. If I'm just using "white" as my backgroundColor, the transition doesn't freeze, it only freezes once I add the image.
Here is a gif of what it looks like...
https://gfycat.com/waryagileichidna
Here is the extension that I'm calling in my viewDidLoad to set the background image...
extension UIView {
func addBackground(image:String) {
self.backgroundColor = .white
// screen width and height:
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
let imageViewBackground = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
imageViewBackground.image = UIImage(named: "\(image)")
imageViewBackground.alpha = 0.5
// you can change the content mode:
imageViewBackground.contentMode = UIView.ContentMode.scaleAspectFill
self.addSubview(imageViewBackground)
self.sendSubviewToBack(imageViewBackground)
}
}
Could you try adding the below line to your addBackground method which you call in viewDidLoad.
imageViewBackground.clipsToBounds = true
I have a feeling this should solve it. That doesn't look like a freeze.

Constrain Button Programmatically To Bottom Center Of Tab Bar

I am building a tab bar with a prominent middle button for adding a post. The issue I'm running into is that my button fits well on iPhone 11 Pro Max but is not positioned correctly on other size iPhones (see images).
I think the issue is that I'm setting the Y position absolutely instead of relative to the tabBar. I am confused on how to do this since I am adding the button programmatically instead of through the storyboard (where I know how to use relative constraints). Here is where I am setting the position:
override func viewDidLayoutSubviews() {
button.frame = CGRect.init(x: self.tabBar.center.x - 32, y: self.view.bounds.height - 115, width: 64, height: 64)
}
How can I set a relative position programmatically for my button so it is always half above and half below the tab bar, regardless of phone size?
try this
let tabBarHeight = 64
let mainButton: UIButton = UIButton(type: .custom)
mainButton.frame = CGRect(origin: CGPoint(x: 0.0, y: win.frame.size.height),size: CGSize(width: tabBarHeight, height: tabBarHeight))
mainButton.center = CGPoint(x: win.center.x, y: win.frame.size.height - tabBar.layer.bounds.height)
you set the size and set the center of the button to the center of the TabBar or move a little up like this code do.
Just add another tabitem in the storyboard builder and then make:
tabbar.clipsToBounds = false
This is the best solution.

Can't manipulate subview properties after it has been added to superview - iOS Swift

I'm trying to add a UIActivityIndicatorView to a button. The problem is, once the activity indicator view is added as a subview, I can no longer do anything with it. Here's my code below:
saveAiv = UIActivityIndicatorView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))
saveAiv.color = .white
saveAiv.startAnimating()
saveAiv.isHidden = false
startQuizButton.addSubview(saveAiv)
self.view.addSubview(startQuizButton)
If I call startAnimating() and set isHidden before the subview is added, it works, but if I try to do anything with the activity view after it's added it has no effect. What is going on here?

Frame on ViewController

I have this class:
extension UIViewController {
func waiting() -> UIView{
let strLabel = UILabel(frame: CGRect(x: 50, y: 0, width: 200, height: 50))
strLabel.text = "Aguarde..."
strLabel.textColor = UIColor.whiteColor()
let messageFrame = UIView(frame: CGRect(x: view.frame.midX - 90, y: view.frame.midY - 25 , width: 180, height: 50))
messageFrame.layer.cornerRadius = 15
messageFrame.backgroundColor = UIColor(white: 0, alpha: 0.40)
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.White)
activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
activityIndicator.startAnimating()
messageFrame.addSubview(activityIndicator)
messageFrame.addSubview(strLabel)
view.addSubview(messageFrame)
return messageFrame
}
}
When I need use this class I use:
class MyController: UIViewController{
....
func x(){
let messageFrame = waiting()
//my code
messageFrame.removeFromSuperview()
}
}
The problem is when the frame is showed if I touch anywhere on my app this frame is hidden. I need that when this frame is showed other options staying disabled, when I finish the frame, the options of app is enabled again. How can I do it?
Change the view that you are returning a bit:
Make a "container" view that has the same frame as the view controller's view. Give this view a "clear" background.
Make a "background" view, same size as the "container" view and add it to the container. Give this view a black background and an alpha of, say, 0.4.
Now put the view that you are currently building in your "waiting" method, and add it to the "container" view (NOT to the background view, this should be a sibling of the background view, otherwise your alert will also be transparent).
Make sure all user interaction is disabled on your views (might only need it disabled on the "container" view).
This gives you a container view that covers the entire screen, and it has 2 children: a transparent background of the same size, and your current "waiting" alert thing.
Now if you add the "container" view to your view controller's view, the user can only touch on the container view which does nothing.
(Keep in mind that this doesn't handle screen rotation...you will have to do that yourself if needed.)

Resources