My application has a UIViewController where uses the new .pageSheet modal presentation style introduced on iOS 13.
This UIViewController has a UINavigationBar on the top and it's pinned, by constraints, at the top, leading and trailing.
I noticed that the background from this view bleeds in white while the UIViewController is animating. Take a look on this recorded GIF from a real device:
Is there anything I can do to solve this? The UIViewController and UINavigationBar were created programatically.
Maybe doing this can solve it?
override func layoutSubviews() {
super.layoutSubviews()
var originalFrame = frame
frame = originalFrame
}
I am using Swift 5.1 and Xcode 11.3. The iPhone is running iOS 13.1.3.
Probably you set the view's layer to rasterize, this is making with the .pageSheet animation not draw it correctly.
The solution, remove the code below:
navigationBar.layer.shouldRasterize = true
navigationBar.layer.rasterizationScale = UIScreen.main.scale
Related
I'd like to implement a nav style like what is found in the "Add Reminder" view controller from Apple's Reminders (iOS 14). I've tried hooking into the scrollview delegate methods but I'm not sure how to change the alpha of the default nav bar background/shadow image.
I've tried changing the nav bar style on scroll and, while that works, it doesn't fade in/out like in the example. That makes me think the answer lies manipulating the alpha value. Thanks in advance!
I've found a (hacky) solution that works in iOS 14 (untested in other versions). It makes an assumption about the private view structure of UINavigationBar, so there's no guarantee that it will work in future iOS versions, but it's unlikely to crash - the worst that should happen is that the bar will fail to hide, or only partially hide.
Assuming that you are placing the code inside a UIViewController subclass that it acting as the delegate for a UITableView, UICollectionView or UIScrollView, the following should work:
override func viewDidLoad() {
super.viewDidLoad()
// this hides the bar initially
self.navigationController?.navigationBar.subviews.first?.alpha = 0
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard let navigationController = self.navigationController else { return }
let navBarHeight = navigationController.navigationBar.frame.height
let threshold: CGFloat = 20 // distance from bar where fade-in begins
let alpha = (scrollView.contentOffset.y + navBarHeight + threshold) / threshold
navigationController.navigationBar.subviews.first?.alpha = alpha
}
The magic threshold value is a little hard to explain, but it's basically the distance from the bar at which the fade in will start. A value of 20 means the bar starts to fade in when the scrollView content is 20 points away. A value of 0 would mean the bar snaps straight from fully transparent to fully opaque the moment the scrollView content touches it.
I have an activity indicator that gets presented on an iPhone and iPad. In the iPad in split screen mode it gets presented to whichever side of the view that called it. I would instead like it to get presented in the middle/center the window's screen. If I do it this way wether on the iPhone in portrait or iPad in split screen mode it will always be in the center of the screen.
How do I do this?
MyView: UIViewController{
let actInd = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
#IBAction fileprivate func buttonPressed(_ sender: UIButton) {
guard let window = UIApplication.shared.keyWindow else { return }
//how to add actInd as subview to the window' screen?
actInd.startAnimating()
}
}
It's pretty simple. Turn off the auto-resizing mask. Add the add actInd to window, then set the center anchors.
actInd.translatesAutoresizingMaskIntoConstraints = false
window.addSubview(actInd)
actInd.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
actInd.centerYAnchor.constraint(equalTo: window.centerYAnchor).isActive = true
Window is subclass of UIView. Just add it as it's subview like you're adding a view to another view. But remember that window is shared throughout your app, so adding it every-time will consume memory, remove it after your job is done.
If you want to center it in the window, you can use autoResizingMask or add constraints to it.
I'm creating an IBDesignable component so that I can see the component render live in the Xcode storyboard. The component is simply a composition of other components. Unfortunately, one of those components requires a UIViewController to function properly. Yes, bad, but I have no control over it.
The component works fine running in the app, but does not render properly at design time because the UIViewController for that scene is not available. I have tried
marching up the responder chain looking for a UIViewController
creating an IBOutlet on the component and connecting it to the View
Controller in the storyboard
Neither of these has worked; the View Controller is always nil. Any suggestions?
A UIViewController can not be render in IBDesignable Mode.
You just can IBDesignable in a view staff.
sample:
import UIKit
#IBDesignable
class SMButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = 6
layer.borderColor = UIColor.baseInstagram.cgColor
layer.borderWidth = 0.66
titleLabel?.font = UIFont.systemFont(ofSize: 15)
titleLabel?.textColor = UIColor.baseInstagram
}
}
The sample is a round button with border.
Unfortunately none of the IBDesignable component's surrounding environment can influence the component itself.
I've used a custom UINavigationController class to put a colour gradient across the navbar. The custom navclass determines the frame size of the navbar, then puts the gradient inside. Problem is, when I rotate from portrait to landscape, the gradient only fills the portrait portion of the landscape bar. I've only assigned the custom UINavigationController class to the navigation view in the storyboard.
So I'm guessing I somehow need to call a refresh for the frame size in the custom navclass when a rotation is done, but I'm not sure how or where?
Here's the relevant code snippet, from the override func viewDidLoad() of the custom navclass.
let gradientlayer = CAGradientLayer()
gradientLayer.frame = self.navigationBar.bounds
self.navigationBar.layer.inserSublayer(gradientLayer, atIndex: 1)
I tried putting it inside viewWillAppear(), but that didn't work. Any help?
viewDidLoad() and viewWillAppear() don't get called on rotation, so your code won't update the frame there.
You should add gradientLayer.frame = self.navigationBar.bounds to viewWillLayoutSubviews() but leave the other code in viewDidLoad() so you don't wind up with multiple gradient layers.
Using the Interface Builder, I created a view with a UIScrollView in it.
I programmaticly add the buttons to the empty UIScrollView.
When the orientation changes, I use
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
to call a method that resets the buttons on screen.
After that, the uiScrollView-content gets a new size using setContentSize.
No matter the width of the ContentSize, I can only interact (scroll/or tap a button) on the first 320px of the screen - which is the screen width in portrait mode.
When I set the contentSize-width to 2000, I can scroll to the left, but only with my fingers on the first 320 px instead of the full 480 (using a 3.5 inch iPhone).
What am I missing?
You need to resize the scrollview's frame, not just the content size (in fact, often you don't need to resize the content size, as the content may not change following an orientation change, but just the frame).
I had the same problem with a Popup (UIView) that was called from a (UIViewController).
In the UIViewController the popup was created as followed
func createPopup() {
let myPopup = MyPopup(frame: UIScreen.mainScreen().bounds)
self.view.addSubview(myPopup)
}
In the Popup (UIView) I needed to override layoutSubviews as follows:
override func layoutSubviews() {
super.layoutSubviews()
self.setNeedsDisplay()
let mainScreenBounds = UIScreen.mainScreen().bounds
view.frame = mainScreenBounds
super.frame = mainScreenBounds <-- Need to update the parent
** perform additional rotation/orientation code here **
}