In my UINavigationController, I add an UIView by code.
I want to remove this UIView when my View disappear but I can't find the right way to do that. Here's the I'm using:
var patch: UIView!
override func viewDidLoad() {
super.viewDidLoad()
patch = UIView(frame: CGRectMake(0, 0, view.bounds.width, 20))
patch.backgroundColor = UIColor.redColor()
self.view.addSubview(patch)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(true)
patch.removeFromSuperview()
// or
self.view.willRemoveSubview(patch)
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
patch.hidden = true
} else {
patch.hidden = false
}
}
and nothing, view is still there.
I tried even setting patch to nil or to CGRectMake(0, 0, 0, 0) but nothing...
What's the right code to delete it?
Related
I want to change the layout and constraints relative to the orientation of the device.
Here's how I implemented it:
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
addViews()
constraintSubviews()
}
func constraintSubviews() {
if UIDevice.current.orientation.isPortrait {
NSLayoutConstraint.activate(portraitConstrains)
NSLayoutConstraint.deactivate(landscapeConstrains)
NSLayoutConstraint.activate(nowPlayingView.portraitConstrains)
NSLayoutConstraint.deactivate(nowPlayingView.landscapeConstrains)
} else {
NSLayoutConstraint.deactivate(portraitConstrains)
NSLayoutConstraint.activate(landscapeConstrains)
NSLayoutConstraint.activate(nowPlayingView.landscapeConstrains)
NSLayoutConstraint.deactivate(nowPlayingView.portraitConstrains)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
constraintSubviews()
}
Here's what I ended up when running the code:
And this is what I expect:
I can't get the layout I designed in the code unless I change device orientation once.
Better implementation
I've searched all over the Internet and the best thing I found was this implementation. Is there a better implementation regarding this subject?
You need
1- Make deactivate before activate
func constraintSubviews() {
if UIDevice.current.orientation.isPortrait {
NSLayoutConstraint.deactivate(landscapeConstrains)
NSLayoutConstraint.deactivate(nowPlayingView.landscapeConstrains)
NSLayoutConstraint.activate(nowPlayingView.portraitConstrains)
NSLayoutConstraint.activate(portraitConstrains)
} else {
NSLayoutConstraint.deactivate(portraitConstrains)
NSLayoutConstraint.deactivate(nowPlayingView.portraitConstrains)
NSLayoutConstraint.activate(landscapeConstrains)
NSLayoutConstraint.activate(nowPlayingView.landscapeConstrains)
}
}
2- Replace
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
constraintSubviews()
}
with
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.willTransition(to: size, with: coordinator)
constraintSubviews()
}
I am loading a nib file of a custom UIView Subclass into my viewcontroller. the problem here is , when i change orientation to landscape, the view still shows in portrait frames. I want it's frame to be set in landscape mode also.
below is my code:-
class CustomPopUpView: UIView {
#IBOutlet weak var vWtitle: UIView!
#IBOutlet weak var lblTitle: UILabel!
#IBOutlet weak var lblContent: UILabel!
#IBOutlet weak var vwAlertSub: UIView!
#IBOutlet weak var btnCenter: UIButton!
var view: UIView!
func xibSetup() {
view = loadViewFromNib()
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.flexibleWidth, UIViewAutoresizing.flexibleHeight]
addSubview(view)
lblTitle.adjustsFontSizeToFitWidth = true
view.backgroundColor = UIColor.black.withAlphaComponent(0.6)
self.showAnimate()
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of: self))
let nib = UINib(nibName: "CustomPopUpView", bundle: bundle)
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
override func layoutSubviews(){
super.layoutSubviews()
print("LayoutSubViews...............")
// view.frame = bounds
}
func showAnimate()
{
self.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.alpha = 0.0;
UIView.animate(withDuration: 0.25, animations: {
self.alpha = 1.0
self.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
});
}
func removeAnimate()
{
UIView.animate(withDuration: 0.25, animations: {
self.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished)
{
self.removeFromSuperview()
}
});
}
init(frame: CGRect, titleTxt:String, contentText: String, leftButtonText:String, rightButtonText:String) {
super.init(frame: frame)
xibSetup()
lblTitle.text = titleTxt
lblContent.text = contentText
if leftButtonText.characters.count > 0 {
btnLeft.isHidden = false
btnLeft.setTitle(leftButtonText,for: .normal)
}
else {
btnLeft.isHidden = true
}
if rightButtonText.characters.count > 0 {
btnRight.isHidden = false
btnRight.setTitle(rightButtonText,for: .normal)
}
else {
btnRight.isHidden = true
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
#IBAction func btnCenterTapped(_ sender: Any) {
self.removeAnimate()
}
Do I need to set the frame once again in layoutsubviews method? Or is there any other method for UIView orientation change? (just like we have for viewcontroller's orientation method "viewWillLayoutSubviews")
I think set the frame once again not correct since auto-layouts came out.
in swift
override func layoutSubviews() {
super.layoutSubviews()
self.setNeedsDisplay()
}
since layoutSubviews() is a View Method .
or you can observe in viewDidLoad NSNotificationCenter.defaultCenter().addObserver(self, selector: "functionThatYouWantTriggeredOnRotation", name:
UIDeviceOrientationDidChangeNotification, object: nil)
Alternatively you can just provide the override for viewWillTransition and not have to add an observer for the UIDeviceOrientationDidChangeNotification.
override func viewWillTransitionToSize(size: CGSize,
withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator)
{
if UIDevice.currentDevice().orientation.isLandscape {
//do your thing
}
}
This method will help you for grabbing orientation change.So if you want to update something during landscape as for your nib height or something, change your code by using this method.
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.currentDevice().orientation.isLandscape.boolValue {
print("Landscape")
//Change your code here for landscape
} else {
print("Portrait")
//Change your code here for portrait
}
}
Swift 3 version:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
}
Swift 4.2 version:
This is the one way to deal with this problem, inside your UIView subclass.
First you need to set observer in your UIView subclass.
override init(frame: CGRect) {
super.init(frame:frame)
NotificationCenter.default.addObserver(self, selector: #selector(updateLayout), name: UIDevice.orientationDidChangeNotification, object: nil)
//UI setup
}
Than in your targer function you can set new frame for your custom view.
#objc func updateLayout(){
if UIDevice.current.orientation.isLandscape {
self.frame = CGRect(.....)
} else {
self.frame = CGRect(......)
}
}
Dontr forget to remove your obsserver in deinit method of your UIView.
deinit {
NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)
}
I know that this question was asked before and tried to implement the suggested solution, but it doesn't work for me. Perhaps I am doing smth wrong. Do you have any idea?
Here is my code:
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var test: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
test.backgroundColor = UIColor.darkGray
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) // No need for semicolon
func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
if UIDevice.current.orientation.isLandscape {
test.backgroundColor = UIColor.purple
} else {
test.backgroundColor = UIColor.blue
}
}
}
}
You have nested functions - that won't work. Change it to this:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated) // No need for semicolon
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
test.backgroundColor = UIColor.purple
} else {
test.backgroundColor = UIColor.blue
}
}
In fact, you can get rid of the override for viewWillLayoutSubviews with the code you've shown.
** to those that need additional help see recommendations below
** this function will be called before the change in size and but you will know it from the arguments
Swift 3.x +
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
//Landscape
print("before transition")
print("w "+"\(width)")
print("h "+"\(height)")
print("to size")
print("w "+"\(size.width)")
print("h "+"\(size.height)")
} else {
//Portrait
print("before transition")
print("w "+"\(width)")
print("h "+"\(height)")
print("to size")
print("w "+"\(size.width)")
print("h "+"\(size.height)")
}
}
There is a Main View with subview. I need to get these subviews.
For example, I created a view:
With the Swift Code:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Check if has view in rect area
let rect1 = CGRect(x: 0, y: 0, width: 100, height: 100)
if CGRectContainsPoint(rect1, rect1.origin) { // return true
print("TRUE")
} else {
print("FALSE")
}
}
}
I need to get the view you are "touching" the Rect in size or origin.
In the above example, I created three subviews with different colors between them.
I need to get the view and make a print() with the backgroundColor.
Can someone help me?
This func fix the problem:
func intersectingViewWithView(panView: UIView) -> UIView? {
for view in self.view.subviews {
if view != panView {
if CGRectIntersectsRect(panView.frame, view.frame) {
return view
}
}
}
return nil
}
I implemented ENSwiftSideMenu in my app, (you can just use the sample project it provided, because it has the same problem,) and when I rotate the iPhone to portrait mode, the menu tableView has a gap between the nav bar and the cells
I found the problem. It's at the following file: MyMenuTableViewController.swift
tableView.contentInset = UIEdgeInsetsMake(64.0, 0, 0, 0)
So I replaced the height of 64 with the height of the navbar:
override func viewDidLoad() {
super.viewDidLoad()
tableView.contentInset = UIEdgeInsetsMake(self.navigationController!.navigationBar.frame.height, 0, 0, 0) // Error
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
tableView.contentInset = UIEdgeInsetsMake(self.navigationController!.navigationBar.frame.height, 0, 0, 0)
}
When I run the app, I get the following error at the code in viewDidLoad:
fatal error: unexpectedly found nil while unwrapping an Optional value
That error before the first page loads (at the splash screen). What can I do to fix that? Or if there's a better way to remove the gap please let me know. I already tried adding the following in viewDidLoad:
self.automaticallyAdjustsScrollViewInsets = true
And that doesn't do anything.
Ok, I've been looking how ENSwiftSideMenu works. It looks like your side menu isn't added to the navigation stack, so if you call self.navigationController, it returns nil always.
Also, ENSwiftSideMenu uses a custom navigation controller: ENSideMenuNavigationController that have a property called sideMenu, which unfortunately, inherits from NSObject (not from UIViewController). So basically, what I had to do was to change MyNavigationController class so it holds a reference to the MyMenuTableViewController used as a side menu, and apply content insets to it's tableview based on the ENSideMenuNavigationController's navigation bar size.
Lastly, you need to apply side table view's contentInset after the rotation was made, so you need to observe UIDeviceOrientationDidChangeNotification notification.
This is how MyNavigationController class should be to work:
class MyNavigationController: ENSideMenuNavigationController, ENSideMenuDelegate {
private let menuTableViewController = MyMenuTableViewController()
override func viewDidLoad() {
super.viewDidLoad()
sideMenu = ENSideMenu(sourceView: self.view, menuViewController: menuTableViewController, menuPosition:.Left)
//sideMenu?.delegate = self //optional
sideMenu?.menuWidth = 180.0 // optional, default is 160
//sideMenu?.bouncingEnabled = false
// make navigation bar showing over side menu
view.bringSubviewToFront(navigationBar)
menuTableViewController.tableView.contentInset = UIEdgeInsetsMake(self.navigationBar.frame.height + self.navigationBar.frame.origin.y, 0, 0, 0)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "rotated", name: UIDeviceOrientationDidChangeNotification, object: nil)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
NSNotificationCenter.defaultCenter().removeObserver(self, name: UIDeviceOrientationDidChangeNotification, object: nil)
}
func rotated() {
menuTableViewController.tableView.contentInset = UIEdgeInsetsMake(self.navigationBar.frame.height + self.navigationBar.frame.origin.y, 0, 0, 0)
}
// MARK: - ENSideMenu Delegate
func sideMenuWillOpen() {
println("sideMenuWillOpen")
}
func sideMenuWillClose() {
println("sideMenuWillClose")
}
func sideMenuDidClose() {
println("sideMenuDidClose")
}
func sideMenuDidOpen() {
println("sideMenuDidOpen")
}
}