UIView doesn't center properly in UIScrollView with autolayout - ios

I'm making an application where I need an x amount of custom UIView and place them in a scrollView. The layout of the scrollView and the UIView xib are set with AutoLayout. The result I'm getting now is this:
First View is well centred in ScrollView
Second View is wrong and has a lot of space in between
See my VC Code under here.
let sponsors = Sponsors.createSponsors() <-- Array
override func viewDidLoad() {
super.viewDidLoad()
configureSponsors()
}
//MARK: Load the AddView in ScrollView
func configureSponsors() {
self.scrollView.contentSize = CGSizeMake(CGFloat(sponsors.count) * self.scrollView.frame.size.width, self.scrollView.frame.size.height)
for sponsor in sponsors {
numberOfItems++
let addView = NSBundle.mainBundle().loadNibNamed("AddView", owner: self, options: nil).last as! AddView
addView.addDataToAddView(sponsor)
addView.frame = CGRectMake(CGFloat(numberOfItems - 1) * scrollView.frame.size.width, 0, scrollView.frame.size.width, scrollView.frame.size.height)
self.scrollView.addSubview(addView)
}
}
And here is my UIView code:
//MARK: Outlets
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var sponsorTitle: UILabel!
#IBOutlet weak var sponsorLogo: UIImageView!
#IBOutlet weak var sponsorSubtitle: UILabel!
#IBOutlet weak var roundView: UIView!
#IBOutlet weak var playMusicButton: UIButton!
//MARK: Properties
var cornerRadius: CGFloat = 3.0
func addDataToAddView(sponsor: Sponsors) {
backgroundImageView.image = UIImage(named: sponsor.backgroundImage)
sponsorLogo.image = UIImage(named: sponsor.logoImage)
sponsorTitle.text = sponsor.title
sponsorSubtitle.text = sponsor.subTitle
}
override func layoutSubviews() {
roundView.layer.cornerRadius = roundView.frame.size.width / 2
roundView.alpha = 0.7
roundView.clipsToBounds = true
}
//MARK: PlayVideo
#IBAction func playVideo(sender: UIButton) {
//play music
}
I've already searched for the same problems. I found out that I have to use the Pure Auto Layout Approach. Should that mean I need to programatically set the constraints for the UIView?
Many thanks,
Dax
Update:
Pictures for scrollView setup:

You should not perform layout calculations in viewDidLoad as frames are not set correctly till then.
Correct place to do this is either viewWillLayoutSubviews or viewDidLayoutSubviews as you will get the correct frame data when these functions are called

Related

Custom xib not sized correctly inside superview

I have a bit of a complicated UI structure.
I have a collection view. For the collection view's cells, I have a custom xib. Inside that custom xib, I load another custom xib into a UIView.
Here is a picture of my collection view cell's custom xib:
https://i.stack.imgur.com/GQLpj.png
I have also highlighted the UIView (Video Container View) that I load another xib into. Here is what that xib looks like:
https://i.stack.imgur.com/jpSi0.png
With the collection view, I do some custom resizing of the cells by making the view 16:9 ratio:
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
// Get the width of the screen
let width = UIScreen.main.bounds.width
let imageWidth = width
let imageHeight = imageWidth / (16 / 9)
self.postMediaHeight = imageHeight
return CGSize(width: imageWidth, height: imageHeight)
}
My PostMediaCollectionViewCell class looks like this:
class PostMediaCollectionViewCell: UICollectionViewCell {
#IBOutlet weak var container: UIView!
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var moreView: UIView!
#IBOutlet weak var moreViewLabel: UILabel!
#IBOutlet weak var videoContainerView: VideoContainerView!
override func awakeFromNib() {
super.awakeFromNib()
}
func initialize() {
//
}
}
And my VideoContainerView class looks like this:
class VideoContainerView: UIView {
#IBOutlet var rootView: UIView!
#IBOutlet weak var videoView: VideoView!
#IBOutlet weak var playIconContainer: UIView!
#IBOutlet weak var timeContainer: UIView!
#IBOutlet weak var timeLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
initialize()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
initialize()
}
func initialize() {
Bundle.main.loadNibNamed("VideoContainerView", owner: self, options: nil)
rootView.translatesAutoresizingMaskIntoConstraints = false
addSubview(rootView)
rootView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
rootView.topAnchor.constraint(equalTo: topAnchor).isActive = true
rootView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
rootView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}
}
But when I run the simulator, the VideoContainerView doesn't seem to fit the superview's (the cell's) constraints/frame, as you can see in the screenshot below. First, there's a white space above the video, and second, the video's resolution is stretch past the container.
One interesting thing I noticed is that if I scroll down past the cell and then scroll back up, it shows the video perfectly.
What am I doing wrong, and what am I missing here?
Update
I just printed out the bounds of a number of views and noticed that the videoView bounds are different from its parents bounds:
rootview bounds: (0.0, 0.0, 661.0, 410.0)
videoview bounds: (0.0, 0.0, 654.0, 593.0)
What should I do here?
You are missing constraints while adding rootView as subview of VideoContainerView. In initialize add following constraints.
func initialize() {
Bundle.main.loadNibNamed("VideoContainerView", owner: self, options: nil)
rootView.translatesAutoresizingMaskIntoConstraints = false
addSubview(rootView)
rootView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
rootView.topAnchor.constraint(equalTo: topAnchor).isActive = true
rootView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
rootView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
}
container is the main view of VideoContainerView
Change initialize method code as below and try to run your code
func initialize() {
let view = Bundle.main.loadNibNamed("VideoContainerView", owner: self, options: nil)?.first as! UIView
addSubview(view)
container.frame = self.bounds
container.autoresizingMask = [.flexibleWidth,.flexibleHeight]
}

Resize a UIView inside a stackview

I need help to accomplish this:
Its a group of 3 photos. The first photo is the button screen before the click, the second photo is the screen after the click and the third photo is the way i've designed in the IB using stackview
I've being trying to create this and this is the result i've got so far. I still haven't created anything as the After button click image shows because of this:
My Result now
As you can see in the gif when i press the button, the UIView height constraint.constant is set to a higher value and the UIView get higher. All i want is the stripped background to get higher as well.
This is the way the view is disposed in IB.
And finally, this is the way i've coded
class LetterScreenViewController: UIViewController, ResizeWordViewDelegate {
#IBOutlet weak var youTubeView: YouTubeView!
#IBOutlet weak var phonemeView: PhonemeView!
#IBOutlet weak var wordView: WordView!
var letterPresenter: LetterPresenter?
#IBOutlet weak var heightWordViewConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
delegateWordObj()
if let presenter = letterPresenter {
presenter.setupView()
}
}
#IBAction func dismissLetter(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
func delegateWordObj() {
wordView.resizeWordViewDelegate = self
}
func didPressButton() {
if heightWordViewConstraint.constant <= 0 {
heightWordViewConstraint.constant = 30
}else{
heightWordViewConstraint.constant = 0
}
}
}
import UIKit
protocol ResizeWordViewDelegate {
func didPressButton()
}
class WordView: UIView {
#IBOutlet weak var backgroundListras: UIImageView!
#IBOutlet var WordView: UIView!
#IBOutlet weak var showWordButton: UIButton!
var resizeWordViewDelegate: ResizeWordViewDelegate!
override init(frame: CGRect) {
super.init(frame: frame)
xibInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibInit()
}
func xibInit() {
Bundle.main.loadNibNamed("WordView", owner: self, options: nil)
addSubview(WordView)
WordView.frame = self.bounds
WordView.round(corners: [.topLeft,.topRight], radius: 120)
WordView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
}
#IBAction func showWordButtonAction(_ sender: Any) {
resizeWordViewDelegate.didPressButton()
self.frame.size = CGSize(width: 375, height: 200)
self.backgroundListras.frame.size = CGSize(width: 375, height: 200)
}
}
I want to know a way to resize this UIView with all the content in it. After finding a solution to increase the height i'll be able to put all the others components shown when i press the button.
I believe that your goal is to create something like this:
All I had to do was change the height of the constraint programmatically
Check the solution in the project below:
https://gitlab.com/DanielLimaDF/ResizeTest.git
Important: Beware of addSubview, it can remove constraints from a View if the constraints were created before calling addSubview

Access ScrollView Delegate Method in another Class

I have a UIScrollView Hooked up to 2 View Controllers. like this
let V1 = self.storyboard?.instantiateViewControllerWithIdentifier("HomeScreen") as UIViewController!
//Add initialized view to main view and its scroll view and also set bounds
self.addChildViewController(V1)
self.scrollVieww.addSubview(V1.view)
V1.didMoveToParentViewController(self)
V1.view.frame = scrollVieww.bounds
//Initialize using Unique ID for the View
let V2 = self.storyboard?.instantiateViewControllerWithIdentifier("MainScreen") as UIViewController!
//Add initialized view to main view and its scroll view also set bounds
self.addChildViewController(V2)
self.scrollVieww.addSubview(V2.view)
V2.didMoveToParentViewController(self)
V2.view.frame = scrollVieww.bounds
//Create frame for the view and define its urigin point with respect to View 1
var V2Frame: CGRect = V2.view.frame
V2Frame.origin.x = self.view.frame.width
V2.view.frame = V2Frame
//The width is set here as we are dealing with Horizontal Scroll
//The Width is x3 as there are 3 sub views in all
self.scrollVieww.contentSize = CGSizeMake((self.view.frame.width) * 2, (self.view.frame.height))
Now i can use The ScrollView Delegate Method which is written inside this class.
So now i went up to my class Main_Screen and subclass it with UIScrollViewDelegate.
Code for Main_Screen Class
import UIKit
public class Main_Screen: UIViewController , UIScrollViewDelegate {
#IBOutlet weak var aboutMebtn: UIButton!
#IBOutlet weak var studybtn: UIButton!
#IBOutlet weak var appsbtn: UIButton!
#IBOutlet weak var skillsbtn: UIButton!
var scrollView : UIScrollView!
#IBOutlet var avatarImageView: UIImageView!
weak var pageControl: UIPageControl!
var mainRead : Bool = false
override public func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
// Making the view round
for v : UIView in self.view.subviews {
if v.tag != 1 {
v.layer.cornerRadius = v.frame.size.width/2
v.layer.masksToBounds = true
}
}
}
public func animate(){
// I get a fatal error here
var buttons : Array<UIButton> = [aboutMebtn , studybtn , appsbtn , skillsbtn]
avatarImageView.alpha = 0.0
avatarImageView.transform = CGAffineTransformMakeScale(1.25, 1.25)
UIView.animateWithDuration(1.0, delay: 1.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .CurveEaseIn, animations: { () -> Void in
self.avatarImageView.alpha = 1.0
self.avatarImageView.transform = CGAffineTransformMakeScale(0.75, 0.75)
}, completion: nil)
}
public func scrollViewDidEndDecelerating(scrollView: UIScrollView) {
if(scrollView.contentOffset.x == self.view.frame.size.width * 1 ){
print("Main is read")
}
}
But the method of UIScrollView Delegate wont execute. i can do it in ViewController but i cant as i want to run the animate function Above on the scrollViewDidEndDeclerating Method
you must assign your view controller as UIScrollView delegate
self.scrollVieww.delegate = self;
you probably miss it.
you can make it inside viewDidLoad

Linking text views to scroll together

I have two side by side text views. How can I link them so that if one is scrolled down other automatically scrolls the same as first one in swift 2.
Set your viewController conforms to UITextViewDelegate,then set textView delegate to self,then in scrollViewDidScroll sync both contentOffSet
For example
Gif
Code
class ViewController: UIViewController,UITextViewDelegate {
#IBOutlet weak var textview2: UITextView!
#IBOutlet weak var textview1: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
textview1.delegate = self
textview2.delegate = self
// Do any additional setup after loading the view, typically from a nib.
}
func scrollViewDidScroll(scrollView: UIScrollView) {
if scrollView == textview1{
textview2.contentOffset = textview1.contentOffset
}else{
textview1.contentOffset = textview2.contentOffset
}
}
}

Custom view won't add subview

I have a view controller that's inheriting a view from it's preceding view, like a floating, draggable view controller with a video mini player (like the YouTube app). When I show the second vc, I'm actually hiding its own view before adding all of its subviews to a new, custom view so it seems like the first view controller is still present.
This new, custom view is adding my UIButton, but not my two custom UIViews. One of these custom views contains a tableview, which is nowhere to be found. I'm concerned this may be a constraint problem, where I need to add constraints to the new view programmatically or something like that. Any ideas?
onView = first controller's view
class DetailViewController: UIViewController, UIGestureRecognizerDelegate {
var initialFirstViewFrame: CGRect!
var onView: UIView!
var isExpandedMode: Bool!
var player: UIWebView!
var youtubeFrame: CGRect!
var tblFrame: CGRect!
var restrictOffset: CGFloat!
var restrictTrueOffset: CGFloat!
var restictYaxis: CGFloat!
var transparentView: UIView!
#IBOutlet weak var viewTable: UIView!
#IBOutlet weak var tblView: UITableView!
#IBOutlet weak var viewYouTube: UIView!
#IBOutlet weak var btnDown: UIButton!
override func viewDidLoad() {
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.8 * Double(NSEC_PER_SEC)))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.calculateFrames()
})
var pan: UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: "panAction")
pan.delegate = self
self.viewYouTube.addGestureRecognizer(pan)
isExpandedMode = true
self.btnDown.hidden = true
}
func calculateFrames() {
youtubeFrame = self.viewYouTube.frame
tblFrame = self.viewTable.frame
UIApplication.sharedApplication().setStatusBarHidden(true, withAnimation: UIStatusBarAnimation.Slide)
self.viewYouTube.translatesAutoresizingMaskIntoConstraints() == true
self.viewTable.translatesAutoresizingMaskIntoConstraints() == true
self.viewYouTube.frame = youtubeFrame
self.viewTable.frame = tblFrame
self.player.backgroundColor = UIColor.purpleColor()
self.viewYouTube.backgroundColor = UIColor.greenColor()
restrictOffset=self.initialFirstViewFrame.size.width-200;
restrictTrueOffset = self.initialFirstViewFrame.size.height - 180;
restictYaxis=self.initialFirstViewFrame.size.height-self.viewYouTube.frame.size.height
self.view.hidden = true
transparentView = UIView(frame: self.initialFirstViewFrame)
transparentView.backgroundColor = UIColor.blackColor()
transparentView.alpha = 0.9
self.onView.addSubview(transparentView)
/////// self.viewTable and self.viewYouTube don't load. They have AutoLayout constraints in storyboard and I'm trying to add their views to a different UIView/////////////
self.onView.addSubview(self.viewTable)
self.onView.addSubview(self.viewYouTube)
self.onView.addSubview(self.btnDown)
}

Resources