Hide view in iOS and rearrange other view with fullscreen - swift

Let's assume I have this layout design on an iPhone Portrait.
A Label (red) and an Image (blue).
But I would like to have it this way when the iPhone is in landscape. Only the image (blue).
I trying with following code but the label does not appear after return back to portrait:
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) -> Void in
let orient = UIDevice.current.orientation
switch orient {
case .portrait:
print("Portrait")
let ctop = NSLayoutConstraint(item: self.imageView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 44)
self.view.removeConstraint(ctop)
self.labelView.isHidden = false
break
default:
print("LandScape")
let ctop = NSLayoutConstraint(item: self.imageView, attribute: .top, relatedBy: .equal, toItem: self.view, attribute: .top, multiplier: 1, constant: 0)
self.view.addConstraint(ctop)
self.labelView.isHidden = true
break
}
}, completion: { (UIViewControllerTransitionCoordinatorContext) -> Void in
print("rotation completed")
})
}

You have a mixup with your addConstraint/removeConstraint logic. In the Portrait case you define a new cTop constraint and remove it (!?!) from the view - you probably want to add it.
It would be better to define both constraints right away when you create your views, and only set one to active - and then switch the isActive setting on both according to your new orientation. Or only define one constraint and just change the constant on it accordingly.

Related

How to add bannerAds add in to pop up view swift 4

App crash when banner ads into pop up view
Terminating app due to uncaught exception 'NSGenericException',
reason: 'Unable to install constraint on view. Does the constraint
reference something from outside the subtree of the view? That's
illegal. constraint:
viewads.addConstraints(
[NSLayoutConstraint(item: bannerView,
attribute: .bottom,
relatedBy: .equal,
toItem: bottomLayoutGuide,
attribute: .top,
multiplier: 1,
constant: 0),
NSLayoutConstraint(item: bannerView,
attribute: .centerX,
relatedBy: .equal,
toItem: view,
attribute: .centerX,
multiplier: 1,
constant: 0)
]) popview.addSubview(bannerView)
banner add in to pop view
Here's how I managed to add a banner to my app:
1. Add this to your ViewDidLoad
bannerView = GADBannerView(adSize: kGADAdSizeSmartBannerPortrait)
addBannerViewToView(bannerView)
bannerView.delegate = self
bannerView.adUnitID = "ca-app-pub-3940256099942544/2934735716" //This is the test ID, always use this to test your app.
bannerView.rootViewController = self
bannerView.load(GADRequest())
2. Add these functions, you can find them on the google AdMob website, I personally suggest to add them all, before I added them my code didn't work
func addBannerViewToView(_ bannerView: GADBannerView) {
bannerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(bannerView)
if #available(iOS 11.0, *) {
// In iOS 11, we need to constrain the view to the safe area.
positionBannerViewFullWidthAtBottomOfSafeArea(bannerView)
}
else {
// In lower iOS versions, safe area is not available so we use
// bottom layout guide and view edges.
positionBannerViewFullWidthAtBottomOfView(bannerView)
}
}
// MARK: - view positioning
#available (iOS 11, *)
func positionBannerViewFullWidthAtBottomOfSafeArea(_ bannerView: UIView) {
// Position the banner. Stick it to the bottom of the Safe Area.
// Make it constrained to the edges of the safe area.
let guide = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
guide.leftAnchor.constraint(equalTo: bannerView.leftAnchor),
guide.rightAnchor.constraint(equalTo: bannerView.rightAnchor),
guide.bottomAnchor.constraint(equalTo: bannerView.bottomAnchor)
])
}
func positionBannerViewFullWidthAtBottomOfView(_ bannerView: UIView) {
view.addConstraint(NSLayoutConstraint(item: bannerView,
attribute: .leading,
relatedBy: .equal,
toItem: view,
attribute: .leading,
multiplier: 1,
constant: 0))
view.addConstraint(NSLayoutConstraint(item: bannerView,
attribute: .trailing,
relatedBy: .equal,
toItem: view,
attribute: .trailing,
multiplier: 1,
constant: 0))
view.addConstraint(NSLayoutConstraint(item: bannerView,
attribute: .bottom,
relatedBy: .equal,
toItem: bottomLayoutGuide,
attribute: .top,
multiplier: 1,
constant: 0))
}
/// Tells the delegate an ad request loaded an ad.
func adViewDidReceiveAd(_ bannerView: GADBannerView) {
print("adViewDidReceiveAd")
addBannerViewToView(bannerView)
bannerView.alpha = 0
UIView.animate(withDuration: 1, animations: {
bannerView.alpha = 1
})
}
/// Tells the delegate an ad request failed.
func adView(_ bannerView: GADBannerView,
didFailToReceiveAdWithError error: GADRequestError) {
print("adView:didFailToReceiveAdWithError: \(error.localizedDescription)")
}
/// Tells the delegate that a full-screen view will be presented in response
/// to the user clicking on an ad.
func adViewWillPresentScreen(_ bannerView: GADBannerView) {
print("adViewWillPresentScreen")
}
/// Tells the delegate that the full-screen view will be dismissed.
func adViewWillDismissScreen(_ bannerView: GADBannerView) {
print("adViewWillDismissScreen")
}
/// Tells the delegate that the full-screen view has been dismissed.
func adViewDidDismissScreen(_ bannerView: GADBannerView) {
print("adViewDidDismissScreen")
}
/// Tells the delegate that a user click will open another app (such as
/// the App Store), backgrounding the current app.
func adViewWillLeaveApplication(_ bannerView: GADBannerView) {
print("adViewWillLeaveApplication")
}
Don't forget to set GADBannerViewDelegate in your class.

How to put a uiview back in a stackview after I took it out?

I have a viewcontroller that holds multiple stackviews. There is a button that when pressed, the corresponding uiview wil become fullscreen inside the original view. There is a different button that is supposed to make the uiview go back to its original stackview. The uiview itself contains other views. I am having some problems doing that. The uiview does end up in the stackview, but not near the same size/place it used to be. I am not sure how to solve this, and been going at it for several hours now, looking at multiple sources.
This is the code that makes the uiview go fullscreen:
private func moveToFrontOfCardView(v: UIView) {
originalView = v.superview
if let stack = originalView as? UIStackView {
stack.removeArrangedSubview(v)
}
myCardView.addSubview(v)
let topConstraint = NSLayoutConstraint(item: v, attribute: .top, relatedBy: .equal, toItem: myCardView, attribute: .top, multiplier: 1, constant: 10)
let bottomConstraint = NSLayoutConstraint(item: v, attribute: .bottom, relatedBy: .equal, toItem: myCardView, attribute: .bottom, multiplier: 1, constant: -10)
let leftConstraint = NSLayoutConstraint(item: v, attribute: .left, relatedBy: .equal, toItem: myCardView, attribute: .left, multiplier: 1, constant: 10)
let rightConstraint = NSLayoutConstraint(item: v, attribute: .right, relatedBy: .equal, toItem: myCardView, attribute: .right, multiplier: 1, constant: -10)
myCardView.addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint])
}
And this is the code I use when I want it to go back:
private func moveToOriginalPosition(v: UIView) {
if let stack = originalView as? UIStackView {
stack.addArrangedSubview(v)
}
}
Does anyone have a clue how I could fix this?
EDIT
I've tried Saqib and Bilals answer, but I get this as a result:
Declare a class variable for tracking view's index
var selectedIndex = 0 // Contains Current Seleceted view's index
overrie func viewDidLoad() { ...
Before removing view from stackview get the view index like this selectedIndex = stack.subviews.index(of: v)
keep reference to all the constraints.
Before adding it back disable all the constraints topConstraint.isActive = false
Now add the view at the same index using stack.insertArrangedSubview(view, at: selectedIndex)
An other option is to create a same new view and just hide/unhide the one in stackview. StackView automatically fills the space accordingly for the hidden views.
You should deActivate the constraints you added to view when removed it from stackView, at the time you want add the view to the stackView again.
For this you should make the constraints instance of your viewController class and next, write your moveToOriginalPosition(v: UIView) method like this:
private func moveToOriginalPosition(v: UIView) {
if let stack = originalView as? UIStackView {
stack.addArrangedSubview(v)
topConstraint.isActive = false
bottomConstraint.isActive = false
leftConstraint.isActive = false
rightConstraint.isActive = false
}
}
Ofcourse, you should remove, these lines of codes from moveToFrontOfCardView(v: UIView) method:
self.topConstraint = NSLayoutConstraint(item: v, attribute: .top, relatedBy: .equal, toItem: myCardView, attribute: .top, multiplier: 1, constant: 10)
self.bottomConstraint = NSLayoutConstraint(item: v, attribute: .bottom, relatedBy: .equal, toItem: myCardView, attribute: .bottom, multiplier: 1, constant: -10)
self.leftConstraint = NSLayoutConstraint(item: v, attribute: .left, relatedBy: .equal, toItem: myCardView, attribute: .left, multiplier: 1, constant: 10)
self.rightConstraint = NSLayoutConstraint(item: v, attribute: .right, relatedBy: .equal, toItem: myCardView, attribute: .right, multiplier: 1, constant: -10)
myCardView.addConstraints([topConstraint, bottomConstraint, leftConstraint, rightConstraint])
and add them where you make your view initialized. and replace below lines with above lines in moveToFrontOfCardView(v: UIView) method:
topConstraint.isActive = true
bottomConstraint.isActive = true
leftConstraint.isActive = true
rightConstraint.isActive = true
By the looks of things you don't need to remove the original view. You could make a copy of it then display the copy full screen. Then when you dismiss this copy you release the reference to it

Swift3 setting constraints relative to parent view of parent view

I have 3 classes, one is a a normal UIViewController in in which I have this code:
let CC0 = CustomUIView0()
self.addSubview(CC0)
the CustomUIView00 is a UIView class containing this code:
let CC1 = CustomUIView1()
self.addSubview(CC1)
Now for CC1 I would like to activate a constraint like this
NSLayoutConstraint.activate([
NSLayoutConstraint(item: CC1, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: ("Parent view of CC0"), attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
])
What I tried to do is to use the UIScreen.main in the toItem place but this throws an error saying that the item needs to be an instance of UIView for this to work.
So I would essentially like to create a constraint relative to the parent of the paren view, how could I do this.
Update:
UIViewController:
let CC0 = UICustomUIView0()
self.view.insertSubview(self.CC0, at: 0)
UICustomUIView0:
init {
super.init(frame: UIScreen.main.bounds);
let CC1 = UICustomUIView1()
guard let CC1Parent = self.CC1.superview, let CC0Parent = CC1Parent.superview else {
print("Problem")
return
}
self.view.addSubview(CC1)
NSLayoutConstraint.activate([
NSLayoutConstraint(item: CC1, attribute: NSLayoutAttribute.top, relatedBy: NSLayoutRelation.equal, toItem: CC0Parent, attribute: NSLayoutAttribute.top, multiplier: 1, constant: 0)
])
}
This throws the problem print
Try below:
// guard below ensures you have the parent AND grandparent views
guard let cc1Parent = cc1.superview, let cc0Parent = cc1Parent.superview else {
// problem with accessing parent views
return
}
// now just set your constraints as per below
NSLayoutConstraint(item: cc1, attribute: .top, relatedBy: .equal, toItem: cc1Parent, attribute: .top, multiplier: 1, constant: 0)
EDIT: For UIView put code in awakeFromNib()
override func awakeFromNib() {
super.awakeFromNib()
// Set layout constraints here
}

UIPageControl has background on tvOS

In one of my games I am using a UICollectionView as the level select menu. I recently added a UIPageControl to it programatically.
/// Page control
func setupPageControl() {
pageControl.hidesForSinglePage = true
pageControl.numberOfPages = DataSource.worlds
pageControl.translatesAutoresizingMaskIntoConstraints = false
pageControl.currentPageIndicatorTintColor = DataSource.pageControlColors[pageControl.currentPage]
pageControl.pageIndicatorTintColor = UIColor.white.withAlphaComponent(0.8)
pageControl.addTarget(self, action: #selector(didPressPageControl), for: .valueChanged)
view.addSubview(pageControl)
let leading = NSLayoutConstraint(item: pageControl, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1, constant: 0)
let trailing = NSLayoutConstraint(item: pageControl, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1, constant: 0)
let bottomConstant = Device.isPad ? view.frame.width / 9 : view.frame.width / 17
let bottom = NSLayoutConstraint(item: pageControl, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: -bottomConstant)
view.addConstraints([leading, trailing, bottom])
}
Everything is fine on iOS but on tvOS the PageController has a translucent background that stretches all across the screen.
How can I turn this off? I tried setting the pageControl background color but that does not seem to work.
As usual just when I post a question I find the answer a minute later.
You can remove the background on tvOS by calling this
#if os(tvOS)
for subview in pageControl.subviews {
let effectView = subview as? UIVisualEffectView
effectView?.removeFromSuperview()
}
#endif
Change background color for page control

Swift - resignFirstResponder resets contentOffset on my UITextView. How come?

So I have a pretty simple app I am trying to build. Its basically a scrollable text field that fills up the entire screen.
When the user taps the screen, the keyboard appears and you can begin to edit on the line where you tapped.
If you tap in the area where the keyboard is going to appear, the size of the textView shrinks so you aren't entering text behind the keyboard.
Everything up until here works. Ill put the code at the bottom.
When the user is done editing, there is a 'Done' button in the upper right hand of the screen, When they press that, the keyboard should disappear, and, if the amount of text is more than what fits on the screen, whichever line they were editing should be at the bottom of the screen.
Right now, no matter what I try, when I resignFirstResponder to hide the keyboard, the textView resets the contentOffset to (0,0)
When I am in the above view, I press done, and this is the result:
what I would like to happen is where I was editing to be at the bottom of the screen, like this:
Class variables so they can be accessed from anywhere in the file
var textField: UITextView = UITextView()
var withKeyboard: NSLayoutConstraint!
var withoutKeyboard: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(textField)
textField.scrollEnabled = true
textField.selectable = true
textField.bounces = true
textField.contentMode = UIViewContentMode.Bottom
textField.setTranslatesAutoresizingMaskIntoConstraints(false)
self.withoutKeyboard = NSLayoutConstraint(item: self.textField, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: 0)
self.view.addConstraint(NSLayoutConstraint(item: self.textField, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Top, multiplier: 1, constant: 0))
self.view.addConstraint(self.withoutKeyboard)
self.view.addConstraint(NSLayoutConstraint(item: self.textField, attribute: NSLayoutAttribute.Left, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Left, multiplier: 1, constant: 0))
self.view.addConstraint(NSLayoutConstraint(item: self.textField, attribute: NSLayoutAttribute.Right, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Right, multiplier: 1, constant: 0))
}
func keyboardWillShow(notification: NSNotification){
doneButton.hidden = false
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.CGRectValue() {
keyboardHeight = keyboardSize.height
self.withKeyboard = NSLayoutConstraint(item: self.textField, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.view, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: -keyboardHeight)
self.view.removeConstraint(withoutKeyboard)
self.view.addConstraint(withKeyboard)
textField.layoutIfNeeded()
}
}
func keyboardWillHide(notification: NSNotification){
self.doneButton.hidden = true
self.textFieldOffset = self.textField.contentOffset.y - self.keyboardHeight
println(self.textFieldOffset)
self.view.removeConstraint(withKeyboard)
self.view.addConstraint(withoutKeyboard)
textField.layoutIfNeeded()
textField.contentOffset.y = self.textFieldOffset
}
func donePressed(){
textField.resignFirstResponder()
createRecord()
}
I made the withKeyboard and withoutKeyboard constraints so that I could take one off and add the other one whenever the keyboard appears/disappears.
Anyway, when I hit the done button, it resets the view to the very top. This isnt all of the code, its just the parts that are giving me trouble.