0

I know this is a common problem having spent many days browsing similar problems and proposed solutions but, having tried everything I can find, I still can't get this work.

Here's what I'm trying to do:

  1. I have a master-detail view where all of the data is pulled from a remote server.
  2. Everything possible is done in the story board.
  3. In my detail view (called Main View) I have a scroll view (Scroll View) tied to the 4 edges of the Main View.
  4. In the Scroll View I have a content view (contentView) with all 4 edges tied to the Scroll View. The contentView width and height are also tied to the Main View width and height as I understand this is required so the Scroll View can correctly calculate its height.
  5. In the contentView I have 2 text labels added in the storyboard with their content added programmatically from the remote server. The first is pinned to the top of the content view and the second is pinned to the bottom of the first label.
  6. Also in the contentView, underneath the second text label, I programmatically add a number of images from the remote server. The number is variable but the height of each image is set programmatically. Each image is pinned to the bottom of the second label with a calculated distance so the images are spaced one above the other.
  7. So far all of this works. I can see the text labels and the images correctly positioned and the vertical scrolling is working. BUT, the content view (shown with a red background) is not resizing vertically so that some of the image appear outside the content view. The Scroll View is shown with a blue background: Screen shot.
  8. I'm seeking guidance on the principles to apply to make this work, but have now included the code so someone might be able to spot where I'm going wrong. Any suggestions please?

    import UIKit
    class DetailViewController: UIViewController {
    @IBOutlet var mainView: UIView!
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var contentView: UIView!
    @IBOutlet weak var routeDescriptionLabel: UILabel!
    @IBOutlet weak var routeDetailLabel: UILabel!
    @IBOutlet weak var detailNavigationBar: UINavigationItem!
        // Create new RouteModel
    var routeModel:RouteModel = RouteModel()
    var imageNumber:Int = 0
    let imageHeight:Int = 200
    
    // Create a variable to hold the bottom constraint for the last route image so it can be tied to the bottom of the contentView in order for the scroll view to calculate its height.
    var lastImageBottomConstraint:NSLayoutConstraint = NSLayoutConstraint()
    
    var detailItem: AnyObject? {
        didSet {
            // Update the view.
        self.configureView()
    
    }
     }
    
     func configureView() {
    
    if let routeID = self.detailItem {
        if let label = self.routeDescriptionLabel {
    
            //contentView.translatesAutoresizingMaskIntoConstraints = false
    
            // Get the route details from the route model for this specified routeID
    
            var selectedRoute:Route = Route()
            selectedRoute = self.routeModel.getRoute(routeID as! Int)
    
            // Get the array of route image dictionaries for this route id
    
            var routeImageDictArray:[NSDictionary] = [[String:String]]()
            routeImageDictArray = routeModel.getRouteImages(Int(routeID as! NSNumber))
    
            // Format the view
    
            detailNavigationBar.title = selectedRoute.routeName
            label.text = selectedRoute.routeDesc
            label.sizeToFit()
    
            self.routeDetailLabel.text = selectedRoute.routeDetails
            self.routeDetailLabel.sizeToFit()
    
            // Loop through the array of route images and add each image to the current view
    
            for image in routeImageDictArray {
    
                // Get the image file name
                var imageFileName:String = String()
                if var imageFileName = image["imageFile"]{
    
                    // Handle any spaces etc in the image file name
                    imageFileName = imageFileName.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
    
                    // Get the image and add it to the view
                    getRouteImage(imageFileName as! String)
                }
            }
    
            // Add lastImageBottomConstraint from the last image loaded to the view so that the scroll view can correctly calculate its height
            self.contentView.addConstraint(self.lastImageBottomConstraint)
    
        }
    }
     }
    
     override func viewDidLoad() {
         super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    self.configureView()
     }
    
     override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
     }
    
     func getRouteImage(imageFileName:String)  {
    // Set the route image
    
    ImageLoader.sharedLoader.imageForUrl("http://www.example.com/files/images/" + imageFileName, completionHandler:{(image: UIImage?, url: String) in
    
        // Create an image view for the image
        var imageView = UIImageView.init(frame: CGRectZero)
    
        // Set translatesautoresizingmask to false
        imageView.translatesAutoresizingMaskIntoConstraints = false
    
        // Increment the image number
        self.imageNumber++
    
        imageView.image = image
    
        // Add the image view to the content view
        self.contentView.addSubview(imageView)
    
        // Add size constraints
        self.applySizeConstraintsToImage(imageView)
    
        // Add position constraints
        self.applyPositionConstraintsToImage(imageView)
    
        imageView.contentMode = UIViewContentMode.ScaleAspectFit
    
        // Create a position constraint to tie the bottom of the last image to the bottom of the view so that the scroll view can size correctly
    
        self.lastImageBottomConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: self.contentView, attribute: NSLayoutAttribute.BottomMargin, multiplier: 1, constant: 20)
    })
     }
    
     func applySizeConstraintsToImage(imageView:UIImageView) {
    
    // set contstraints for the imageview
    let heightConstraint:NSLayoutConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Height, relatedBy: NSLayoutRelation.Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: CGFloat(imageHeight))
    
    imageView.addConstraint(heightConstraint)
    
    
     }
    
     func applyPositionConstraintsToImage(imageView:UIImageView) {
    
    // Set the position of the imageview
    let verticalConstraint:NSLayoutConstraint = NSLayoutConstraint(item: imageView, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: routeDetailLabel, attribute: NSLayoutAttribute.Bottom, multiplier: 1, constant: CGFloat((20 * (imageNumber)) + (imageHeight * (imageNumber-1))))
    
    let centerXConstraint = NSLayoutConstraint(item: imageView, attribute: .CenterX, relatedBy: .Equal, toItem: contentView, attribute: .CenterX, multiplier: 1, constant: 0);
    
    // Add position constraints to the image
    self.contentView.addConstraints([verticalConstraint,centerXConstraint])
    
          }
       }
    
Mitch
  • 111
  • 10
  • http://stackoverflow.com/a/22489795/2664531 – Darko Jan 21 '16 at 21:22
  • Thanks @Darko, I'm still testing this out but the main problem is that my content is dynamic (the text labels are loaded from a remote server so their height is not fixed). The suggested approach has fixed height contents so I'm not sure it will work? – Mitch Jan 23 '16 at 15:14
  • You can change the contentSize property also during runtime. – Darko Jan 23 '16 at 15:49
  • OK, but I would have to be able to calculate the size of every item in the content? I can set the image sizes but what about the text labels (UILabel) - how do I know the height of a text label when the content is dynamic? – Mitch Jan 23 '16 at 16:41
  • You set the text and call sizeToFit, afterwards you can get the correct frame height. – Darko Jan 23 '16 at 16:43
  • Thanks again @Darko, your suggestions are definitely edging me nearer a solution but I'm still not quite there. I have added my code so my problem might be easier to spot. – Mitch Jan 23 '16 at 19:55
  • Sorry, but that's to much for me currently. Another question: why don't you use a simple tableview? It handles all the complexity for you, including dynamic cell-height due to text labels. – Darko Jan 24 '16 at 19:21
  • Good question @Darko, I'll think about that approach - it could be a better way forward. Thanks again for your efforts. – Mitch Jan 25 '16 at 17:34

0 Answers0