self.tableView.tableFooterView = PromptInviteView.instantiateFromNib()
I've tried putting this in viewWillAppear and viewDidLoad.
Great, everything shows up and displays correctly.
However, if:
I push HOME and open 4-5 other apps, then return back to my app, the footer will "jump" to the top, covering the first row.
If I perform other actions inside the app (navigating back and forth), and then finally going back to his vc, then it will jump to the top also.
When I pull to refresh, all is good again. But if I don't do anything...the footer stays at the top (replacing the top row + is not clickable)
Does anyone know why this behavior is occurring, and how I can solve it?
Update: I realize that "drawRect" in my xib gets called when I open the app after 4-5 other apps. And this is why the problem is happening.
In my drawRect code, I have this:
override func drawRect(rect: CGRect) {
super.drawRect(rect)
inviteLabel.textColor = UIColor(hex: 0x404040)
inviteLabel.text = STRINGS["PromptInviteLabelText"]
inviteLabel.numberOfLines = 0
inviteLabel.sizeToFit()
inviteButton.setTitle(STRINGS["PromptInviteButtonText"], forState: UIControlState.Normal)
inviteButton.backgroundColor = FlatWatermelon()
inviteButton.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal)
inviteButton.setTitleColor(UIColor(hex: 0xcccccc), forState: UIControlState.Selected)
inviteButton.layer.cornerRadius = 2.0
inviteButton.clipsToBounds = true
super.layoutSubviews() //this will allow the view's frame to be set.
var padding = CGFloat(20.0)
self.frame = CGRectMake(0, 0, screenWidth, CGRectGetMaxY(inviteButton.frame) + padding )
var topBorder = UIView()
topBorder.frame = CGRectMake(0, 0, screenWidth, 0.5)
topBorder.backgroundColor = UIColor(hex: 0xededed)
self.addSubview(topBorder)
var bottomBorder = UIView()
bottomBorder.frame = CGRectMake(0, self.frame.size.height, screenWidth, 0.5)
bottomBorder.backgroundColor = UIColor(hex: 0xededed)
self.addSubview(bottomBorder)
}
How can I remove my code (especially the layoutSubview part) and put it somewhere else?
Is there another location where I should put the code? Is there a didLayoutSubviews() equivalent?
Related
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.
I've been trying for one whole day to make it work right. I tried different tutos and made adjustments (searching in here) but it doesnt work properly.
On iphone 5s, it almost works right, excepts for the last page that it seems to add "half" of a page on scroll view. The green part is the background color of the scroll view and it doesn't bounce back.
On iphone 6, the content is all messed up and instead of having 5 images, it has only 4 and a half (i cant scroll it anymore and it doesn't bounce back).
If I press continue, it works perfectly on both screen sizes. It is only the swipe option that is not working properly.
Here is the code where I put the images inside the scroll view.
func loadContent() {
var frame = CGRect(x: 0, y: 0, width: 0, height: 0)
for i in 0..<imageArr.count { // is 5
frame.origin.x = scrollView.frame.size.width * CGFloat(i)
frame.size = self.scrollView.frame.size
let imageView = UIImageView(frame: frame)
let image = UIImage(named: imageArr[i])
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.backgroundColor = UIColor.red
imageView.image = image
scrollView.addSubview(imageView)
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i)
}
}
///Function of Continue button
#IBAction func nextImage(_ sender: Any) {
UIView.animate(withDuration: 0.3) {
self.scrollView.contentOffset.x = self.scrollView.frame.width * CGFloat(self.pageControl.currentPage+1)
}
}
I've been searching for 4 hours for a solution but nothing seems to work.
[Edit]: With the answer of TheFuquan I could solve the problem.
[Obs:] if you searching about the messed content, these lines of code can help you. All the tutos that I saw didn't have these lines and the content just wouldnt align perfect and this solved:
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight, .flexibleBottomMargin, .flexibleRightMargin, .flexibleLeftMargin, .flexibleTopMargin]
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
Why is the following line in your for loop:
self.scrollView.contentSize.width = self.scrollView.frame.width * CGFloat(i)
You should do this before the for loop like this:
self.scrollView.contentSize.width = self.scrollView.frame.width * imageArr.count
this is a typical problem that i face each time i try to load some dynamic content inside a view.
If you re calling your function loadContent from viewDidLoad, then you should call your loadContent inside viewDidLayoutSubviews
Be aware that unlike viewDidLoad, viewDidLayoutSubviews may get called multiple times, so have a flag to ensure that your loadContent gets invoked only once.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
guard self.didLoadContent == false else {
return
}
self.didLoadContent = true
loadContent()
}
this way you let the view loaded from your storyboard to resize the iPhone's size.
I have read many articles about the SFSafariViewController and I believe that it offers splendid functionality in iOS apps. However, when I load my SFSafariViewController, I intentionally hide the navigation bar because I want one custom fixed button in the upper left corner to dismiss the view controller.
override func viewDidAppear(_ animated: Bool) {
let safariViewController = PSSafariViewController(url: URL(string: blogUrl)!, entersReaderIfAvailable: true)
present(safariViewController, animated: false) {
var frame = safariViewController.view.frame
let OffsetY: CGFloat = 44
frame.origin = CGPoint(x: frame.origin.x, y: frame.origin.y - OffsetY)
frame.size = CGSize(width: frame.width, height: frame.height + OffsetY)
safariViewController.view.frame = frame
let btn: UIButton = UIButton(frame: CGRect(x: 100, y: 400, width: 100, height: 50))
btn.backgroundColor = UIColor.green
btn.setTitle("Click Me", for: .normal)
btn.addTarget(self, action: #selector(PSBlogViewController.buttonAction), for: UIControlEvents.touchUpInside)
btn.tag = 1 // change tag property
btn.isOpaque = true
safariViewController.view.addSubview(btn)
safariViewController.view.bringSubview(toFront: btn)
print(btn.description)
}
}
As you can see, I alter the frame so that the bar at the top is not visible. That code runs fine. But when I try to add a UIButton, it appears briefly and then is covered when I run the app. It's a simple blog reader app that uses the SFSafariViewController. Maybe Apple doesn't want developers running around messing with this, but any solutions or workarounds to make the button stay visible are greatly appreciated!
Here's the info about the button: 0x7f950b618db0; frame = (100 400; 100 50); tag = 1; layer = CALayer: 0x60000023ab60
Why don't you use one of the other WebView classes to get the extra functionalities you desire?
5.1.1 (iv) SafariViewContoller must be used to visibly present information to users; the controller may not be hidden or obscured by
other views or layers. Additionally, an app may not use
SafariViewController to track users without their knowledge and
consent.
It is definitely true that Apple won't want the SFSafariViewController to be obscured. However, I did figure out that when I present it, in the completion block I can add a button and what was causing trouble was that I had to increase the zPosition of the layer of the button's view like so:
present(safariViewController, animated: false) {
var frame = safariViewController.view.frame
let OffsetY: CGFloat = 44
frame.origin = CGPoint(x: frame.origin.x, y: frame.origin.y - OffsetY)
frame.size = CGSize(width: frame.width, height: frame.height + OffsetY)
safariViewController.view.frame = frame
self.btn = UIButton(frame: CGRect(x: 5, y: 50, width: 50, height: 50))
self.btn.layer.cornerRadius = 25
self.btn.backgroundColor = UIColor(red: 0, green: 170/255, blue: 240/255, alpha: 0.5)
self.btn.setTitle("←", for: .normal)
self.btn.addTarget(self, action: #selector(safariViewController.buttonAction(sender:)), for: .touchUpInside)
self.btn.tag = 1 // change tag property
self.btn.isOpaque = true
safariViewController.view.addSubview(self.btn)
safariViewController.view.bringSubview(toFront: self.btn)
self.btn.layer.zPosition = safariViewController.view.layer.zPosition + 1
for subview in safariViewController.view.subviews {
subview.isUserInteractionEnabled = false
}
self.btn.isEnabled = true
self.btn.isUserInteractionEnabled = true
//print(self.btn.description)
}
Ok as the question suggests, I want to make a menu like the spotify app currently have.
I have come across a code in github that resembles this but i want to know how to achieve this on a DIY(Do it yourself) level.
I can understand that a pageview controller is needed but am lost. Please point me to the right direction.
EDIT
So that people might understand properly the requirement I am talking about, Here is a png
Notice the top menu that slides with the underline as the selected menu.
The github link that i posted is a library that does that, but its in swift, so can anyone tell me as to how to achieve this in objective c
Regards
I have made a similar kind of sample but that is in Swift 2.0. Have a look. May be it can be of some help.
In the sample link posted below, the query you are asking can be done in the following way.
We can use this method to setup the buttons first on the top of page view
In your terms this is the Top Menu
func setupSegmentButtons(){
navigationView = UIView(frame: CGRectMake(0, 0, self.view.frame.size.width, (self.navigationController?.navigationBar.frame.size.height)! - 4))
//navigationView.backgroundColor = UIColor.redColor()
navigationView.layoutIfNeeded()
let numControllers:NSInteger = viewControllerArray.count
if(buttonText == nil)
{
buttonText = NSArray(objects: "First","Second","Third")
}
let buttonWidth:CGFloat = self.view.frame.size.width/CGFloat(numControllers)
for (var i = 0; i < numControllers; i++) {
let button:UIButton = UIButton(frame: CGRectMake( CGFloat(i) * buttonWidth, 0, buttonWidth, 40))
button.addTarget(self, action: "tapSegmentButtonAction:", forControlEvents: .TouchUpInside)
navigationView.addSubview(button)
button.tag = i
button.titleLabel?.font = UIFont(name: "Helvetica", size: 14)
button.setTitleColor(UIColor.blackColor(), forState: .Normal)
button.setTitle(buttonText.objectAtIndex(i) as? String, forState: .Normal)
}
self.view.addSubview(navigationView)
self.setupSelector()
}
Now we have to add the selection bar which will slide accordingly with the menu selected.
Setting up the selection bar view
func setupSelector(){
//selection bar view
selectionBar = UIView(frame: CGRectMake(X_BUFFER - X_OFFSET + SELECTED_POSITION , SELECTOR_Y_BUFFER, (self.view.frame.size.width - 2 * X_BUFFER)/CGFloat(viewControllerArray.count), SELECTOR_HEIGHT))
selectionBar.backgroundColor = UIColor.brownColor()
selectionBar.alpha = 0.8
navigationView.addSubview(selectionBar)
}
Now we can use the scroll view's scrollViewDidScroll delegate in order to make the selection bar view slides towards the selected menu. Each time this delegate is called whenever the page view controller is scrolled, we change the frame of selected bar view every moment.
func scrollViewDidScroll(scrollView: UIScrollView) {
let xFromCenter : CGFloat = self.view.frame.size.width-scrollView.contentOffset.x;
let xCoors : CGFloat = X_BUFFER + selectionBar.frame.size.width * CGFloat(currentPageIndex) - X_OFFSET
_ = selectionBar.frame
if(CGFloat(xCoors)-xFromCenter / CGFloat(viewControllerArray.count) > self.view.frame.size.width - selectionBar.frame.width)
{
selectionBar.frame = CGRectMake(self.view.frame.size.width - selectionBar.frame.width, selectionBar.frame.origin.y, selectionBar.frame.size.width, selectionBar.frame.size.height);
}
else if(CGFloat(xCoors)-xFromCenter / CGFloat(viewControllerArray.count) < 0)
{
selectionBar.frame = CGRectMake(0, selectionBar.frame.origin.y, selectionBar.frame.size.width, selectionBar.frame.size.height);
}
else
{
selectionBar.frame = CGRectMake(CGFloat(xCoors)-xFromCenter/CGFloat(viewControllerArray.count), selectionBar.frame.origin.y, selectionBar.frame.size.width, selectionBar.frame.size.height);
}
}
For more details please see the sample link below.
https://github.com/RajanMaheshwari/PageViewController
Video of the issue!
Example of what I mean by Twitter-like UIScrollView:
I basically have it working, but I have this small glaring issue and I don't know where it is coming from. I have checked all the constraints and values for my two view controllers, but something is off.
In short,
The code that creates the NavBar and then populates it with the two ViewControllers side by side:
override func viewDidLoad() {
super.viewDidLoad()
var navBar: UINavigationBar = UINavigationBar(frame: CGRectMake(0, 0, self.view.bounds.width, 64))
navBar.barTintColor = UIColor.blackColor()
navBar.translucent = false
//Creating some shorthand for these values
var wBounds = self.view.bounds.width
var hBounds = self.view.bounds.height
// This houses all of the UIViews / content
scrollView = UIScrollView()
scrollView.backgroundColor = UIColor.clearColor()
scrollView.frame = self.view.frame
scrollView.pagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
scrollView.bounces = false
self.view.addSubview(scrollView)
self.scrollView.contentSize = CGSize(width: self.view.bounds.size.width * 2, height: hBounds)
//Putting a subview in the navigationbar to hold the titles and page dots
navbarView = UIView()
//Paging control is added to a subview in the uinavigationcontroller
pageControl = UIPageControl()
pageControl.frame = CGRect(x: 0, y: 35, width: 0, height: 0)
pageControl.pageIndicatorTintColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 0.3)
pageControl.currentPageIndicatorTintColor = UIColor.whiteColor()
pageControl.numberOfPages = 2
pageControl.currentPage = 0
self.navbarView.addSubview(pageControl)
//Titles for the nav controller (also added to a subview in the uinavigationcontroller)
//Setting size for the titles. FYI changing width will break the paging fades/movement
navTitleLabel1 = UILabel()
navTitleLabel1.frame = CGRect(x: 0, y: 8, width: wBounds, height: 20)
navTitleLabel1.textColor = UIColor.whiteColor()
navTitleLabel1.textAlignment = NSTextAlignment.Center
navTitleLabel1.text = "Title 1"
self.navbarView.addSubview(navTitleLabel1)
navTitleLabel2 = UILabel()
navTitleLabel2.alpha = 0.0
navTitleLabel2.frame = CGRect(x: 100, y: 8, width: wBounds, height: 20)
navTitleLabel2.textColor = UIColor.whiteColor()
navTitleLabel2.textAlignment = NSTextAlignment.Center
navTitleLabel2.text = "Title 2"
self.navbarView.addSubview(navTitleLabel2)
//Views for the scrolling view
//This is where the content of your views goes (or you can subclass these and add them to ScrollView)
feedViewController = storyboard?.instantiateViewControllerWithIdentifier("FeedController") as FeedViewController
view1 = feedViewController.view
addChildViewController(feedViewController)
feedViewController.didMoveToParentViewController(self)
view1.frame = CGRectMake(0, 0, wBounds, hBounds)
self.scrollView.addSubview(view1)
self.scrollView.bringSubviewToFront(view1)
//Notice the x position increases per number of views
secondViewController = storyboard?.instantiateViewControllerWithIdentifier("SecondController") as SecondViewController
view2 = secondViewController.view
addChildViewController(secondViewController)
secondViewController.didMoveToParentViewController(self)
view2.frame = CGRectMake(wBounds, 0, wBounds, hBounds)
self.scrollView.addSubview(view2)
self.scrollView.bringSubviewToFront(view2)
navBar.addSubview(navbarView)
self.view.addSubview(navBar)
}
I've looked at my storyboard and both ViewControllers seem identical in regards to their constraints.
I know this is an issue because both ViewControllers are populated by UITableViews. When I scroll through the SecondViewController, it works perfectly. When I scroll through the FeedViewController, there is a small white space at the top that I can't seem to get rid of and it shows that the text cuts off there. I've been stuck on this for a long time and if there is any other information needed, I'll gladly provide it.
Edit: Included video of the issue. If I could, I would bounty this question right now. I don't understand the cause
Update: After swapping both ViewController positions, I have noticed that the problem does not lie with either ViewController. The problem lies with page 1 being set lower. When swapped, the original SecondViewController also experienced the same behavior
So, I think everyone who implements this runs into this issue at some point. The issue isn't with the first ViewController. Simply adjust the constraint to be 44 from the top. The issue is with the second ViewController and it isn't so much an issue when you understand how they work. Technically, it is off to the side and hence its top constraint does not adhere to the Navigation Bar, so what you have is a constraint - 20. Which, depending on how you originally placed your constraints, can give you this seeming issue.
But basically, anyone and everyone will run into this issue when implementing this.
TL;DR: To make everything seamless, your second, third, fourth, fifth, etc. page View Controllers need a constraint + 20 of your first View Controller. With my set-up, I use a constraint of 44 for my first View Controller and hence 64 for the second