Month: January 2017

  • Pass special characters in JavaScript function

    ในการพัฒนาโปรแกรมไม่ว่าจะพัฒนาด้วยภาษาใดก็ตาม ทุกภาษาจะมี special characters ซึ่ง special characters บางตัวอาจจะถูกระบุให้เป็นตัวแปร หรือ ใช้อักขระพิเศษเพื่อระบุอยู่ลำดับสุดท้ายของข้อความของบรรทัด เป็นต้น   ซึ่งตรงส่วนนี้จะทำให้ประสบปัญหาในการนำ special characters ไปใช้งาน นั่นคือจะทำให้เกิด Error ทำให้โปรแกรมไม่สามารถทำงานต่อได้ ซึ่งJavaScript ก็เป็นอีกหนึ่งภาษาที่ประสบปัญหานี้ เช่น การส่ง parameter ผ่าน function ของ JavaScript โดยค่าของ parameter ที่ส่งไปเป็น special characters ทำให้เกิด Error และโปรแกรมไม่สามารถทำงานต่อได้

     

    วันนี้จะขอแนะนำเทคนิคในการแก้ปัญหาดังกล่าว นั่นก็คือ การ Encoding และ Decoding ในที่นี้ผู้เขียนได้พัฒนาในรูปแบบ MVC โดยใช้ JavaScript ในการเรียก Controller จึงใช้วิธีการ Encoding กับ JavaScript และ ทางฝั่ง Controller จะใช้ C# จึงใช้ C# ในการ Decoding ค่ะ
    1.วิธีการ Encoding in JavaScript ในการจะส่ง parameter ผ่านทาง function ของ JavaScript โดยข้อความนั้นมี special characters จำเป็นที่จะต้องมีการ Encoding ในที่นี้เราจะใช้ฟังก์ชัน Escape() ของ JavaScript
    ตัวอย่าง Encoding in JavaScript

    <script>   
    
      function SaveData() {
    
            var txtDocTitle = $('#txtDocTitle').val();
    
    
            $('#pleaseWaitDialog').modal();
            $.ajax({
                type: 'POST',
                contentType: 'application/json; charset=utf-8',
                url: "@Url.Action("SaveDataToTable", "I_EXPENSE")",
                data: "{'budgetYear':'" + valueYear +
                    "' ,'docTitle':'" + escape(txtDocTitle) +
                    "'}",
                success: function (data) {
    
                },
                error: function (data) {
                    alert(data);
                }
            });
        }
          </script>

    จากตัวอย่างกรณีป้อนข้อมูลที่มี special characters เช่น “I’m *&^$ special characters” เมื่อใช้ function escape(“I’m *&^$ special characters”) จะได้ผลลัพธ์ดังนี้

     ผลลัพธ์จากการเรียกใช้ escape()

    I%u2019m%20*%26%5E%24%20special%20characters
    

    หลังจากที่มีการเรียกใช้ escape() จะทำให้สามารถส่ง parameter ที่มี special characters ไปได้โดยไม่พบ Error โปรแกรมสามารถทำงานได้ปกติ เป็นอันผ่านไปหนึ่งด่านในส่วนของการเรียกใช้งาน function ของ JavaScript แต่ทว่าการที่เราจะนำข้อความที่ผ่านการ Encoding มาใช้งานไม่ได้ เนื่องจากจะไม่ถูกต้องตามความต้องการของผู้ใช้งาน จากตัวอย่างข้างต้น ผู้ใช้งานป้อนข้อมูล “I’m *&^$ special characters” สิ่งที่โปรแกรมต้องทำการบันทึกก็ต้องเป็น “I’m *&^$ special characters” ไม่ใช่ “I%u2019m%20*%26%5E%24%20special%20characters” ดังนั้นจึงจำเป็นจะต้องมีการ Decoding เพื่อให้ได้ข้อมูลที่ถูกต้อง
    2.Decoding in C# เมื่อมีการ Encoding เราจำเป็นที่จะต้อง Decoding เพื่อให้ได้ข้อมูลที่ถูกต้องตามที่ผู้ใช้งานได้ป้อนข้อมูล
    ตัวอย่าง Decoding in C#

          var docTitle = HttpUtility.UrlDecode(docTitle, System.Text.Encoding.Default);
           //ตรงส่วนนี้ตัวแปร docTitle จะมีค่าเท่ากับ “I’m *&^$ special characters”  ค่ะ
          

    เมื่อ Decoding แล้ว ก็จะได้ข้อความที่ถูกต้องตามที่ผู้ใช้งานได้ป้อนข้อมูลและสามารถนำไปใช้งานได้อย่างถูกต้องแล้วนะคะ

     

    สำหรับบทความนี้ก็ขอจบเพียงเท่านี้และหวังว่าผู้พัฒนาคงหมดกังวลในส่วนของ Special character ที่ส่งค่า parameter ผ่านทาง JavaScript ได้อย่างไม่มี error มากวนใจผู้พัฒนากันอีกแล้วนะคะ ^_^

     

     

    แหล่งอ้างอิง :

    https://developer.mozilla.org/th/docs/Web/JavaScript/Reference/Global_Objects/escape

    http://stackoverflow.com/questions/3778165/unescape-javascripts-escape-using-c-sharp

  • Removing duplicate records by using Oracle’s ROWID

    ถ้าคุณมีตารางข้อมูลอยู่ และรู้ว่ามีบางแถวที่มีข้อมูลซ้ำซ้อนกัน ทางไหนเป็นวิธีที่ดีที่จะสามารถหาและกำจัดแถวที่มีข้อมูลซ้ำนี้ออกไปจากตารางของฐานข้อมูล Oracle ?

     

    การหาแถวที่มีข้อมูลซ้ำซ้อน

    เราสามารถหาข้อมูลแถวที่มีข้อมูลซ้ำซ้อนกันได้โดยใช้คำสั่ง select ดังนี้

     

    select a,b,count(*)

    from test

    group by a,b

    having count(*) > 1;

    ผลลัพธ์ที่ได้ :

    A          B   COUNT(*)

    ———- ———- ———-

    1          2        259

    2          2          5

     

    จากตัวอย่างในตาราง test นี้เราจะกำหนดว่าให้ค่าในคอลัมภ์ a และ b จะต้องมีค่าไม่ซ้ำ ซึ่งผลลัพธ์ที่ปรากฏคือ มีข้อมูลซ้ำ 258 แถว และ 4 แถว

     

    การกำจัดแถวที่มีข้อมูลซ้ำซ้อน
    เราสามารถกำจัดแถวที่มีข้อมูลซ้ำซ้อนกันได้โดยการใช้ rowid เข้ามาช่วย คราวนี้คุณต้องเลือกว่าจะเลือกเก็บข้อมูลแถวไหนไว้

    เราลองมาดูข้อมูลที่ควรจะเป็นที่ไม่ซ้ำกันว่ามีข้อมูลอะไรบ้าง โดยสามารถใช้คำสั่งได้ดังนี้

    select a,b,count(*) from test

    group by a,b;

    A          B   COUNT(*)

    ———- ———- ———-

    1          2        259

    2          2          5

    3          0          1

     

    กรณีที่ต้องการลบและคงเหลือไว้เฉพาะแถวแรกที่ซ้ำสามารถใช้คำสั่งได้ดังนี้

     

    เราต้องการกำจัด 258 แถวที่ซึ่ง A = 1 และ B = 2 บวกกับ

    — 4 แถวที่ซึ่ง A = 2 และ B = 2

    ลองมา select แถวที่เราจะคงไว้ดูก่อน

    select min(rowid),a,b from test

    group by a,b;

    MIN(ROWID)                             A          B

    ——————————- ———- ———-

    AAAAyvAAGAAAABYAAA          1          2

    AAAAyvAAGAAAABYAED          2          2

    AAAAyvAAGAAAABYAEI           3          0

     

    — คราวนี้ก็ถึงเวลาลบข้อมูลกันแล้ว

    — เริ่มกันเลย

    delete from test where rowid not in (

    select min(rowid) from test group by a,b);

    262 rows deleted.

     

    — คราวนี้มาตรวจสอบกันว่าข้อมูลที่คงเหลือถูกต้องหรือไม่

    select rowid,a,b from test;

    ROWID                                      A          B

    ——————————— ———- ———-

    AAAAyvAAGAAAABYAAA          1          2

    AAAAyvAAGAAAABYAED          2          2

    AAAAyvAAGAAAABYAEI           3          0

     

    กรณีที่ต้องการลบและคงเหลือไว้เฉพาะแถวสุดท้ายที่ซ้ำสามารถใช้คำสั่งได้ดังนี้

    delete from test where rowid not in (

    select max(rowid) from test group by a,b);

    262 rows deleted.

     

    แต่เพื่อป้องกันการเกิดการซ้ำซ้อนของข้อมูลเหล่านี้ เราสามารถที่จะใช้ unique constraints หรือ primary key ช่วยได้ ปัญหาข้อมูลซ้ำซ้อนแบบนี้ก็จะไม่มีทางเกิดขึ้นให้เกิดความปวดหัวได้อีก

     

  • อีกหนึ่งวิธีในการกำจัดข้อมูลที่ซ้ำซ้อนกันในตาราง

    ถ้าคุณมีตารางข้อมูลอยู่ และรู้ว่ามีบางแถวที่มีข้อมูลซ้ำซ้อนกัน ทางไหนเป็นวิธีที่ดีที่จะสามารถหาและกำจัดแถวที่มีข้อมูลซ้ำนี้ออกไปจากตารางของฐานข้อมูล Oracle ?

    อีกวิธีหนึ่งที่เป็นไปได้ในการกำจัดแถวที่ซ้ำซ้อนกันคือการใช้คำสั่ง select distinct และใส่ข้อมูลที่ได้ลงในตารางใหม่

     

    จากที่เราสามารถตรวจสอบหาข้อมูลแถวที่มีข้อมูลซ้ำซ้อนกันได้โดยใช้คำสั่ง select ดังนี้

    SQL> select a,b,count(*) from test group by a,b;

    ผลลัพธ์ที่ได้

    A           B COUNT(*)
    ———- ———- ———-
    1           2       259
    2           2           5
    3           0           1

    จากตัวอย่างในตาราง test นี้ที่กำหนดไว้ว่าค่าในคอลัมภ์ a และ b จะต้องมีค่าไม่ซ้ำ ซึ่งผลลัพธ์ที่ปรากฏคือ มีข้อมูลซ้ำ 258 แถว และ 4 แถว

     

    เรามาเริ่มต้นกำจัดข้อมูลซ้ำซ้อนอีกวิธีกันเลย

     

    การหาแถวข้อมูลที่ไม่ซ้ำซ้อนกัน

    เราสามารถหาข้อมูลที่ไม่ซ้ำซ้อนกันได้โดยใช้คำสั่ง select distinct ดังนี้

    SQL> select distinct * from test;

    ผลลัพธ์ที่ได้

             A           B
    ———- ———-
              1           2
              2           2
              3           0

     

    สร้างตารางใหม่ชั่วคราวเพื่อเก็บผลลัพธ์ที่ได้

    SQL> create table new_test as (select distinct * from test);

     

    ตรวจสอบผลลัพธ์ที่ได้ในตารางชั่วคราวนี้

    SQL> select * from new_test;

    ผลลัพธ์ที่ได้

             A           B
    ———- ———-
              1           2
              2           2
              3           0 

     

    ทำการลบข้อมูลในตาราง test ทั้งหมด

    SQL> truncate table test;

    Table truncated.

     

    ทำการเพิ่มข้อมูลกลับไปยังตาราง test จากตารางชั่วคราว    

    SQL> insert into test (select * from new_test);

    3 rows created.

     

    ทำการลบตารางข้อมูลชั่วคราวทิ้ง

    SQL> drop table new_test;

    Table dropped.

     

    ตรวจสอบกันอีกครั้งว่าข้อมูลยังซ้ำกันอีกหรือไม่โดยใช้คำสั่ง select ดังนี้

    SQL> select a,b,count(*) from test group by a,b;

    ผลลัพธ์ที่ได้

    A           B COUNT(*)
    ———- ———- ———-
    1           2           1
    2           2           1
    3           0           1

     

    นี่ก็เป็นอีกหนึ่งวิธีในการกำจัดแถวที่มีข้อมูลซ้ำซ้อนกันในตาราง แต่เพื่อป้องกันการเกิดการซ้ำซ้อนของข้อมูลเหล่านี้ ขอย้ำอย่าลืมใช้ unique constraints หรือ primary key ช่วยได้ ปัญหาข้อมูลซ้ำซ้อนแบบนี้ก็จะไม่มีทางเกิดขึ้นให้เกิดความปวดหัวได้อีก

  • การค้นหาสถานที่ด้วย Places search box และแสดงผลภาพจากข้อมูล Street View บน Google Maps

           ความเดิมจากตอนที่แล้วของบทความ “การดึงค่าละติจูดและลองจิจูดของสถานที่ด้วย Places search box บน Google Maps” เพื่อแก้ปัญหาในการดึงค่าละติจูด-ลองจิจูดจากชื่อสถานที่ที่ผู้ใช้พิมพ์ค้นหาไว้ โดยมีการนำ “Places search box” มาใส่ไว้ในแผนที่ที่เราต้องการแทนนั้น ถือเป็นตัวช่วยให้กับผู้ใช้ในระดับหนึ่ง  แต่สำหรับในบทความนี้ ผู้เขียนจะขอต่อยอดความรู้ดังกล่าว โดยได้ศึกษาและนำวิธีการแสดงผลภาพจากข้อมูล Street view มาประยุกต์ใช้ร่วมด้วย เพื่อให้ผู้ใช้สามารถมองเห็นสถานที่จริงที่เลือกได้จากแผนที่ได้ด้วย ซึ่งจะยิ่งช่วยอำนวยความสะดวกให้กับผู้ใช้ ทำให้ผู้ใช้สามารถตรวจสอบได้ว่าสถานที่ดังกล่าวเป็นสถานที่ที่ต้องการจะดึงค่าละติจูด-ลองจิจูดจริงหรือไม่ได้อีกทางหนึ่งนั่นเอง ซึ่งวิธีการบางส่วนจะขอยกยอดมาจากบทความก่อนหน้านี้ เกี่ยวกับเรื่องการดึงค่าละติจูด-ลองจิจูดจากการค้นหาโดยใช้ Places search box และจะขอเพิ่มเติมความสามารถในการแสดงผลภาพ Street View  รวมทั้งสามารถคลิกจุดพิกัดใหม่บนแผนที่ เพื่อค้นหาตำแหน่ง สถานที่ตั้ง และรายละเอียด รวมถึงภาพจาก Street View ได้อีกด้วย เพื่อให้การดึงค่าพิกัดใกล้เคียงความจริงมากที่สุด โดยมีวิธีการดังนี้

    • ส่วนของสไตล์ชีทในการแสดงผล ขึ้นกับการตกแต่งของผู้พัฒนาแต่ละท่าน
    <!—ส่วนของ Style Sheets->
    <style>
    html, body {
     height: 100%;
     margin: 0;
     padding: 0;
    }
    #map {
     width: 100%;
     height: 400px;
    }
    .controls {
     margin-top: 10px;
     border: 1px solid transparent;
     border-radius: 2px 0 0 2px;
     box-sizing: border-box;
     -moz-box-sizing: border-box;
     height: 32px;
     outline: none;
     box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
    }
    #searchInput {
     background-color: #fff;
     font-family: Roboto;
     font-size: 15px;
     font-weight: 300;
     margin-left: 12px;
     padding: 0 11px 0 13px;
     text-overflow: ellipsis;
     width: 80%;
    }
    #searchInput:focus {
     border-color: #4d90fe;
    }
    
    </style>
    
    • ส่วนของการอ้างอิง libraries เพื่อใช้งาน Google API
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCRbMoDPc_mTv3D3QPqe0Ar84nSvRhA8nk&libraries=places&callback=initMap" async defer></script>
    
    • ส่วนของการประกาศค่าเริ่มต้นและตัวแปร รวมถึงการเรียกใช้งานฟังก์ชั่นเพื่อใช้ในการแสดงผล
      • ฟังก์ชั่น initMap() ซึ่งจะเรียกใช้งานตอนเริ่มต้นโหลดหน้าจอขึ้นมา
    <script>
     var panorama;
     var map;
     var markers = [];
    /***** function ในการประกาศค่าเริ่มต้นให้กับแผนที่*****/
     function initMap() {
    
    /***** ประกาศตำแหน่งพิกัดกึ่งกลางให้กับการแสดงผลแผนที่ ในที่นี้กำหนดเป็นกรุงเทพมหานคร*****/
     var center_point = { lat: 13.7563309, lng: 100.50176510000006 };
     var sv = new google.maps.StreetViewService();
    
    /***** เป็นส่วนของการแสดงผลภาพจาก Street View *****/
     panorama = new google.maps.StreetViewPanorama(document.getElementById('pano'));
    
     /***** กำหนดรายละเอียดคุณสมบัติของแผนที่*****/
     var map = new google.maps.Map(document.getElementById('map'), {
     center: {lat: center_point.lat, lng: center_point.lng},
     zoom: 13,
     streetViewControl: false // เป็นส่วนที่ Set เพิ่มเติมจากปกติ
     });
    
    sv.getPanorama({ location: center_point, radius: 50 }, processSVData);
    
    /***** กำหนด event เมื่อมีการคลิกแผนที่*****/
     map.addListener('click', function(event) {
     
    /***** ล้างข้อมูลการกำหนดจุด เพื่อให้มีการกำหนดจุดพิกัดเพียงจุดเดียว*****/
     for (i = 0; i < markers.length; i++) {
     markers[i].setMap(null);
     }
    
    /***** ดึงค่าข้อมูลจาก Street View ตามพิกัดที่คลิกบนแผนที่ *****/
     sv.getPanorama({ location: event.latLng, radius: 50 }, processSVData);
     var geocoder = new google.maps.Geocoder();
     geocoder.geocode({
     'latLng': event.latLng
     }, function(results, status) {
     if (status == google.maps.GeocoderStatus.OK) {
    
    /***** แสดงผลข้อมูลรายละเอียดสถานที่*****/
     if (results[0]) {
        var place = results[0];
         for (var i = 0; i < place.address_components.length; i++) {
           if (place.address_components[i].types[0] == 'postal_code') {
             document.getElementById('postal_code').innerHTML = place.address_components[i].long_name;
          }
     if (place.address_components[i].types[0] == 'country') {
     document.getElementById('country').innerHTML = place.address_components[i].long_name;
        }
      }
     document.getElementById('location').innerHTML = results[0].formatted_address;
     document.getElementById('lat').innerHTML = results[0].geometry.location.lat();
     document.getElementById('lon').innerHTML = results[0].geometry.location.lng();
        }
       }
     });
     });
     /***** กำหนดตำแหน่งที่ตั้งของ control ที่จะวางในแผนที่*****/
     var input = document.getElementById('searchInput');
     map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
    
     /***** เพิ่ม Feature ให้กับ textbox ให้สามารถพิมพ์ค้นหาสถานที่ได้*****/
     var autocomplete = new google.maps.places.Autocomplete(input);
     autocomplete.bindTo('bounds', map);
    
     var infowindow = new google.maps.InfoWindow();
     
     /***** กำหนดคุณสมบัติให้กับตัวพิกัดจุดหรือ marker *****/
     var marker = new google.maps.Marker({
     map: map,
     anchorPoint: new google.maps.Point(0, -29)
     });
    markers.push(marker); //เก็บค่าการกำหนดจุดพิกัดไว้ในตัวแปร markers เพื่อใช้ล้างข้อมูลการกำหนดจุดได้
    
     /***** ทำงานกับ event place_changed หรือเมื่อมีการเปลี่ยนแปลงค่าสถานที่ที่ค้นหา*****/
     autocomplete.addListener('place_changed', function() {
     infowindow.close();
     marker.setVisible(false);
     var place = autocomplete.getPlace();
     if (!place.geometry) {
     window.alert("ไม่ค้นพบพิกัดจากสถานที่ดังกล่าว");
     return;
     }
    
     /***** แสดงผลบนแผนที่เมื่อพบข้อมูลที่ต้องการค้นหา *****/
     if (place.geometry.viewport) {
     map.fitBounds(place.geometry.viewport);
     } else {
     map.setCenter(place.geometry.location);
     sv.getPanorama({ location: place.geometry.location, radius: 50 }, processSVData);
     map.setZoom(17);
     }
     marker.setIcon(({
     url: place.icon,
     size: new google.maps.Size(71, 71),
     origin: new google.maps.Point(0, 0),
     anchor: new google.maps.Point(17, 34),
     scaledSize: new google.maps.Size(35, 35)
     }));
     marker.setPosition(place.geometry.location);
     marker.setVisible(true);
     
     /***** แสดงรายละเอียดผลลัพธ์การค้นหา *****/
     var address = '';
     if (place.address_components) {
     address = [
     (place.address_components[0] && place.address_components[0].short_name || ''),
     (place.address_components[1] && place.address_components[1].short_name || ''),
     (place.address_components[2] && place.address_components[2].short_name || '')
     ].join(' ');
     }
     /***** แสดงรายละเอียดผลลัพธ์การค้นหาเป็น popup โดยมีชื่อและสถานที่ดังกล่าว *****/
     infowindow.setContent('<div><strong>' + place.name + '</strong><br>' + address);
     infowindow.open(map, marker);
    
     /***** แสดงรายละเอียดผลลัพธ์การค้นหา ซึ่งประกอบด้วย ที่อยู่ รหัสไปรษณีย์ ประเทศ ละติจูดและลองจิจูด *****/
     for (var i = 0; i < place.address_components.length; i++) {
     if(place.address_components[i].types[0] == 'postal_code'){
     document.getElementById('postal_code').innerHTML = place.address_components[i].long_name;
     }
     if(place.address_components[i].types[0] == 'country'){
     document.getElementById('country').innerHTML = place.address_components[i].long_name;
     }
     }
     document.getElementById('location').innerHTML = place.formatted_address;
     document.getElementById('lat').innerHTML = place.geometry.location.lat();
     document.getElementById('lon').innerHTML = place.geometry.location.lng();
     });
    }
    </script>
    
    • ฟังก์ชั่น processSVData() เป็นฟังก์ชั่นที่ใช้ในการนำค่าของ Street view มาแสดงผล
    <script>
    function processSVData(data, status) {
     if (status === 'OK') {
     
      marker = new google.maps.Marker({
     position: data.location.latLng,
     map: map,
     title: data.location.description
     });
     markers.push(marker);
     panorama.setPano(data.location.pano);
     panorama.setPov({
     heading: 270,
     pitch: 0
     });
     panorama.setVisible(true);
    
     marker.addListener('click', function() {
     
     var markerPanoID = data.location.pano;
     // Set the Pano to use the passed panoID.
     panorama.setPano(markerPanoID);
     panorama.setPov({
     heading: 270,
     pitch: 0
     });
     panorama.setVisible(true);
     });
     } else {
     console.error('ไม่พบข้อมูล Street view ตามสถานที่ดังกล่าว');
     }
     }
    </script>
    
    • ส่วนของการแสดงผล (ใน tag body)
    <body>
    
    <!--ส่วนของการแสดงรายละเอียดผลลัพธ์ที่ได้->
     <table style="width: 100%">
     <tr>
     <td>
    <!--ส่วนของ Element ที่ใช้ในการแสดงผล->
     <input id="searchInput" class="controls" type="text" placeholder="Enter a location">
    <div id="map" style="width: 60%;height:360px;float:left"></div>
    <div id="pano" style="width: 40%;height:360px; float:left"> </div><br />
     </td>
     
     </tr>
     <tr>
     <td>
     <div id="Div1" style="width: 100%;" >
     <ul id="geoData">
     <li>ที่อยู่: <span id="location"></span></li>
     <li>รหัสไปรษณีย์ : <span id="postal_code"></span></li>
     <li>ประเทศ: <span id="country"></span></li>
     <li>ละติจูด: <span id="lat"></span></li>
     <li>ลองจิจูด: <span id="lon"></span></li>
    </ul>
    </div>
    
     </td>
     </tr>
     
     </table>
     <!--ส่วนของการประกาศค่าเริ่มต้นและตัวแปร รวมถึงการเรียกใช้งานฟังก์ชั่นเพื่อใช้ในการแสดงผลที่กล่าวไว้ก่อนหน้านี้-> <script> /***** function ในการประกาศค่าเริ่มต้นให้กับแผนที่ และอื่นๆตามที่กล่าวไว้แล้วข้างต้น*****/ </script> <!--สิ้นสุดส่วนของการประกาศค่าเริ่มต้นและตัวแปร รวมถึงการเรียกใช้งานฟังก์ชั่นเพื่อใช้ในการแสดงผล-> </body>

    ผลลัพธ์ที่ได้

    1. เมื่อเปิดหน้าจอขึ้นมา จะแสดงพิกัดที่จังหวัดกรุงเทพมหานครเป็นค่าตั้งต้น และฝั่งขวาเป็นภาพที่ดึงข้อมูลมาได้จากพิกัดดังกล่าวจากข้อมูล Street Views

    2. พิมพ์ค้นหาชื่อสถานที่ และเลือกสถานที่ที่ต้องการ

    3. เมื่อเลือกสถานที่เรียบร้อย จะมีการกำหนดพิกัดจุดตามชื่อสถานที่ที่เลือกไว้ และแสดงผลภาพจาก Street View รวมทั้งแสดงรายละเอียดในส่วนล่างของหน้าจอ ดังภาพ

           นอกจากการค้นหาพิกัดและข้อมูลภาพจาก Street View โดยใช้วิธีการพิมพ์ค้นหาแล้วนั้น ผู้ใช้ยังสามารถค้นหาพิกัดใหม่ได้จากการคลิกบนแผนที่ได้อีกด้วย ซึ่งจะแสดงผลลัพธ์ได้เช่นกัน ดังภาพ

           ผู้เขียนหวังเป็นอย่างยิ่งว่า บทความนี้จะช่วยเพิ่มประสิทธิภาพและเป็นอีกหนึ่งทางเลือกให้กับผู้พัฒนาได้หยิบยกวิธีการดึงค่าละติจูด-ลองจิจูดและการแสดงผลข้อมูลไปประยุกต์ใช้กับงานของแต่ละท่าน และอาจถือเป็นการเพิ่มลูกเล่นให้กับเว็บไซต์ของท่านให้มีความสามารถที่หลากหลายมากขึ้นเกี่ยวกับการใช้งานร่วมกับแผนที่ Google Maps หากผู้อ่านท่านใดมีข้อเสนอแนะหรือแนะนำวิธีเพิ่มเติมเกี่ยวกับเรื่องดังกล่าว ผู้เขียนยินดีเป็นอย่างยิ่งที่จะรับฟังและร่วมแบ่งปันความรู้ร่วมกันค่ะ หากบทความนี้มีส่วนผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ด้วยค่ะ ^^

    แหล่งข้อมูลอ้างอิง :  

    https://www.tutorialspoint.com/google_maps/google_maps_symbols.htm

    http://www.codexworld.com/autocomplete-places-search-box-google-maps-javascript-api/

          https://developers.google.com/maps/documentation/javascript/examples/streetview-service

  • การดึงค่าละติจูดและลองจิจูดของสถานที่ด้วย Places search box บน Google Maps

           ก่อนจะพูดถึงเนื้อหาของบทความนี้ ผู้เขียนขอท้าวความไปถึงบทความก่อนหน้าที่เป็นเนื้อหาที่เกี่ยวข้องกับการดึงค่าละติจูด-ลองจิจูดของสถานที่ เพื่อมากำหนดจุดพิกัดบนแผนที่ หรือที่เรารู้จักกันดีในนามของ Google maps (ซึ่งสามารถหาอ่านได้จากลิงค์ ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C# (ภาคต่อ) และหากท่านต้องการศึกษาเกี่ยวกับวิธีการกำหนดพิกัดบนแผนที่เพิ่มเติมสามารถหาอ่านได้จากลิงค์ ทำอย่างไรให้สามารถกำหนดจุดพิกัดบนแผนที่ Google map แบบจุดเดียวและหลายจุดจากฐานข้อมูลได้ด้วย ASP.NET C# เช่นกัน) แต่หลังจากที่ผู้เขียนได้นำไปทดลองใช้งานการดึงค่าพิกัดที่ค้นหา พบว่าเกิดปัญหาในการค้นหาพิกัดของสถานที่ในบางกรณี คือ ไม่สามารถค้นหาพิกัดของบางสถานที่ที่ต้องการได้ และในบางครั้งผู้ใช้เลือกสถานที่ที่จะดึงค่าพิกัดมาใช้งานผิด เนื่องจากชื่อสถานที่อาจคล้ายกัน แต่ตั้งอยู่กันคนละประเทศ หรือทวีป โดยผู้ใช้อาจไม่เห็นภาพว่าสถานที่ดังกล่าวอยู่ส่วนใดของแผนที่ จึงทำให้พิกัดที่ได้มีความผิดพลาดหรือคลาดเคลื่อนได้ ผู้เขียนจึงได้ลองศึกษาเพิ่มเติม และปรับเปลี่ยนวิธีการ เพื่อให้การดึงค่าพิกัดเป็นไปได้ง่าย และอำนวยความสะดวกต่อผู้ใช้งานมากขึ้น รวมทั้งเพิ่มช่องทางในการค้นหาข้อมูลพิกัดได้มากขึ้นและมีความถูกต้องแม่นยำขึ้น

    ตัวอย่างภาพการทำงานของการดึงค่าพิกัดที่ผู้เขียนเคยเขียนไว้ก่อนหน้านี้

    แบบที่ 1 การเรียกใช้เซอร์วิสของ Google Geocoding API โดยการส่งพารามิเตอร์เป็นที่อยู่ของสถานที่ดังกล่าว

       

    แบบที่ 2 แบบใช้ place Autocomplete ซึ่งเป็น feature ของ Google Places API ที่จะช่วยในการค้นหาที่อยู่จากชื่อสถานที่ได้และประยุกต์เพิ่มเติมเพื่อดึงค่ามาแสดงเมื่อมีการเลือกรายการสถานที่นั้นๆ

         

           จากปัญหาดังกล่าวข้างต้น ผู้เขียนได้ปรับเปลี่ยนวิธีการในการดึงค่าละติจูด-ลองจิจูดจากชื่อสถานที่ที่ผู้ใช้พิมพ์ค้นหาไว้ โดยมีการนำ “Places search box” มาใส่ไว้ในแผนที่ที่เราต้องการแทน เพื่อให้ผู้ใช้สามารถมองเห็นสถานที่จริงที่เลือกได้จากแผนที่ ซึ่งสามารถตรวจสอบได้ว่าสถานที่ดังกล่าวเป็นสถานที่ที่ต้องการจะดึงค่าละติจูด-ลองจิจูดจริงหรือไม่อีกทางหนึ่งนั่นเอง โดยมีวิธีการดังนี้

    • ส่วนของสไตล์ชีทในการแสดงผล ขึ้นกับการตกแต่งของผู้พัฒนาแต่ละท่าน
    <!—ส่วนของ Style Sheets-->
    <style>
    html, body {
     height: 100%;
     margin: 0;
     padding: 0;
    }
    #map {
     width: 100%;
     height: 400px;
    }
    .controls {
     margin-top: 10px;
     border: 1px solid transparent;
     border-radius: 2px 0 0 2px;
     box-sizing: border-box;
     -moz-box-sizing: border-box;
     height: 32px;
     outline: none;
     box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
    }
    #searchInput {
     background-color: #fff;
     font-family: Roboto;
     font-size: 15px;
     font-weight: 300;
     margin-left: 12px;
     padding: 0 11px 0 13px;
     text-overflow: ellipsis;
     width: 50%;
    }
    #searchInput:focus {
     border-color: #4d90fe;
    }
    
    </style>
    
    • ส่วนของการอ้างอิง libraries เพื่อใช้งาน Google API
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCRbMoDPc_mTv3D3QPqe0Ar84nSvRhA8nk&libraries=places&callback=initMap" async defer></script>
    
    • ส่วนของการประกาศค่าเริ่มต้นและตัวแปร รวมถึงการเรียกใช้งานฟังก์ชั่นเพื่อใช้ในการแสดงผล
    <script>
    /***** function ในการประกาศค่าเริ่มต้นให้กับแผนที่*****/
     function initMap() {
     
     /***** กำหนดรายละเอียดคุณสมบัติของแผนที่*****/
     var map = new google.maps.Map(document.getElementById('map'), {
     center: {lat: -33.8688, lng: 151.2195},
     zoom: 13
     });
    
     /***** กำหนดตำแหน่งที่ตั้งของ control ที่จะวางในแผนที่*****/
     var input = document.getElementById('searchInput');
     map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
    
     /***** เพิ่ม Feature ให้กับ textbox ให้สามารถพิมพ์ค้นหาสถานที่ได้*****/
     var autocomplete = new google.maps.places.Autocomplete(input);
     autocomplete.bindTo('bounds', map);
    
     var infowindow = new google.maps.InfoWindow();
     
     /***** กำหนดคุณสมบัติให้กับตัวพิกัดจุดหรือ marker *****/
     var marker = new google.maps.Marker({
     map: map,
     anchorPoint: new google.maps.Point(0, -29)
     });
    
     /***** ทำงานกับ event place_changed หรือเมื่อมีการเปลี่ยนแปลงค่าสถานที่ที่ค้นหา*****/
     autocomplete.addListener('place_changed', function() {
     infowindow.close();
     marker.setVisible(false);
     var place = autocomplete.getPlace();
     if (!place.geometry) {
     window.alert("ไม่ค้นพบพิกัดจากสถานที่ดังกล่าว");
     return;
     }
    
     /***** แสดงผลบนแผนที่เมื่อพบข้อมูลที่ต้องการค้นหา *****/
     if (place.geometry.viewport) {
     map.fitBounds(place.geometry.viewport);
     } else {
     map.setCenter(place.geometry.location);
     map.setZoom(17);
     }
     marker.setIcon(({
     url: place.icon,
     size: new google.maps.Size(71, 71),
     origin: new google.maps.Point(0, 0),
     anchor: new google.maps.Point(17, 34),
     scaledSize: new google.maps.Size(35, 35)
     }));
     marker.setPosition(place.geometry.location);
     marker.setVisible(true);
     
     /***** แสดงรายละเอียดผลลัพธ์การค้นหา *****/
     var address = '';
     if (place.address_components) {
     address = [
     (place.address_components[0] && place.address_components[0].short_name || ''),
     (place.address_components[1] && place.address_components[1].short_name || ''),
     (place.address_components[2] && place.address_components[2].short_name || '')
     ].join(' ');
     }
     /***** แสดงรายละเอียดผลลัพธ์การค้นหาเป็น popup โดยมีชื่อและสถานที่ดังกล่าว *****/
     infowindow.setContent('<div><strong>' + place.name + '</strong><br>' + address);
     infowindow.open(map, marker);
    
     /***** แสดงรายละเอียดผลลัพธ์การค้นหา ซึ่งประกอบด้วย ที่อยู่ รหัสไปรษณีย์ ประเทศ ละติจูดและลองจิจูด *****/
     for (var i = 0; i < place.address_components.length; i++) {
     if(place.address_components[i].types[0] == 'postal_code'){
     document.getElementById('postal_code').innerHTML = place.address_components[i].long_name;
     }
     if(place.address_components[i].types[0] == 'country'){
     document.getElementById('country').innerHTML = place.address_components[i].long_name;
     }
     }
     document.getElementById('location').innerHTML = place.formatted_address;
     document.getElementById('lat').innerHTML = place.geometry.location.lat();
     document.getElementById('lon').innerHTML = place.geometry.location.lng();
     });
    }
    </script>
    • ส่วนของการแสดงผล (ใน tag body)
    <body>
    <!--ส่วนของ Element ที่ใช้ในการแสดงผล-->
    <input id="searchInput" class="controls" type="text" placeholder="Enter a location">
    <div id="map"></div>
    <!--ส่วนของการแสดงรายละเอียดผลลัพธ์ที่ได้-->
    <ul id="geoData">
     <li>ที่อยู่: <span id="location"></span></li>
     <li>รหัสไปรษณีย์: <span id="postal_code"></span></li>
     <li>ประเทศ: <span id="country"></span></li>
     <li>ละติจูด: <span id="lat"></span></li>
     <li>ลองจิจูด: <span id="lon"></span></li>
    </ul>
    
    <!--ส่วนของการประกาศค่าเริ่มต้นและตัวแปร รวมถึงการเรียกใช้งานฟังก์ชั่นเพื่อใช้ในการแสดงผลที่กล่าวไว้ก่อนหน้านี้-->
                                                                
    <script>
    /***** function ในการประกาศค่าเริ่มต้นให้กับแผนที่ และอื่นๆตามที่กล่าวไว้แล้วข้างต้น*****/
    </script>
    <!--สิ้นสุดส่วนของการประกาศค่าเริ่มต้นและตัวแปร รวมถึงการเรียกใช้งานฟังก์ชั่นเพื่อใช้ในการแสดงผล->
     </body>

    ผลลัพธ์ที่ได้

    1. เมื่อพิมพ์ชื่อสถานที่ เช่น มหาวิทยาลัยสงขลานครินทร์ จะมีรายการชื่อสถานที่ให้ผู้ใช้เลือก

    2. เมื่อเลือกชื่อสถานที่ที่ต้องการ เช่น มหาวิทยาลัยสงขลานครินทร์ จะมีการแสดงรายละเอียดของสถานที่ดังกล่าวดังภาพตามหมายเลข 1,2 และ 3 ตามลำดับ

           ผู้เขียนหวังเป็นอย่างยิ่งว่า บทความนี้จะช่วยแก้ปัญหาให้กับผู้ที่อาจจะกำลังเจอปัญหาในแบบเดียวกันกับผู้เขียนในการดึงค่าละติจูด-ลองจิจูดเพื่อนำไปกำหนดจุดพิกัดบนแผนที่อยู่ไม่มากก็น้อย หากผู้อ่านท่านใดมีข้อเสนอแนะหรือแนะนำวิธีเพิ่มเติมเกี่ยวกับเรื่องดังกล่าว ผู้เขียนยินดีเป็นอย่างยิ่งที่จะรับฟังและร่วมแบ่งปันความรู้ร่วมกันค่ะ หากบทความนี้มีส่วนผิดพลาดประการใด ผู้เขียนขออภัยไว้ ณ ที่นี้ด้วยค่ะ ^^

    แหล่งข้อมูลอ้างอิง :

    https://www.tutorialspoint.com/google_maps/google_maps_symbols.htm

    http://www.codexworld.com/autocomplete-places-search-box-google-maps-javascript-api/

  • Infographic เล่าเรื่องด้วยภาพ

    ปัจจุบันเราทุกคนจะต้องมีการรับ/ส่งข้อมูลอะไรระหว่างกันอยู่เสมอ และสิ่งนึงที่เรามักจะคิดตรงกันคือเราจะสื่อสารกันอย่างไรให้อีกฝ่ายเข้าใจได้รวดเร็ว และง่ายที่สุด

    “อินโฟกราฟิก” เป็นอีก 1 คำตอบ ที่จะช่วยแก้ไขปัญหานี้ได้

     

    อินโฟกราฟิกคืออะไร ?

    คือการแปลงข้อมูลให้เป็นภาพ เพื่อให้เข้าใจได้ง่าย และสื่อสารกับผู้คนด้วยสิ่งที่จับต้องได้ การที่จะอ่านบทความที่มีความยาวหลายๆหน้า กราฟ

    หรือข้อมูลมหาศาลคงต้องใช้เวลานาน ที่สำคัญบางคนอาจจะไม่สนใจข้อมูลเหล่านั้นเลยก็เป็นได้ เพราะการตีความของคนที่อ่านแต่ละคนไม่เหมือนกัน

     

    ทักษะที่จำเป็นในการทำ อินโฟกราฟิก

    ทักษะพื้นฐาน 3 อย่างคือ ทักษะการวิเคราะห์ ทักษะการเรียบเรียบ และทักษะดีไซน์

     

    หลักการออกแบบอินโฟกราฟิก แบ่งเป็น 2 ส่วน

    1. ด้านข้อมูล

    ข้อมูลที่จะนำเสนอ ต้องมีความหมาย มีความน่าสนใจ เรื่องราว เปิดเผยเป็นจริง มีความถูกต้อง

    1. ด้านการออกแบบ

    การออกแบบต้องมีรูปแบบ แบบแผน โครงสร้าง หน้าที่การทำงานและความสวยงาม โดยออกแบบให้เข้าใจง่าย ใช้งานง่าย และใช้ได้จริง

     

    การทำอินโฟกราฟิกโดยใช้ Web Tool

    Web Tool คือ เว็บที่ให้บริการทำอินโฟกราฟิกออนไลน์ เช่น piktochart หรือ easel.ly เราสามารถเลือก Template แล้วปรับแต่งได้ตามใจชอบ

    โดยจะขอยกตัวอย่างการทำอินโฟกราฟิกโดยการใช้ piktochart

     

    วิธีการสมัครใช้งาน

     

    • กรอกข้อมูล หรือเลือก Sign In ผ่าน Gmail/Facebook เมื่อสมัครเรียบร้อยแล้ว จะได้ดังรูป

     

     

     

     

     

     

     

    • เริ่มต้นสร้าง โดยคลิกเลือก “Start Creating” จากนั้นเข้าสู่หน้าสร้างงานผ่าน piktochart

    • เลือก Template รูปแบบที่ต้องการ เช่น infographic presentation หรือ printable โดยในแต่ละรูปแบบจะมี Free template ให้เลือกใช้งาน
      ตัวอย่าง Free Template Infographic

    • Tool ต่างๆ ของหน้าเว็บที่มีให้ผู้ใช้ได้เลือกใช้งาน
    Menu Graphics

    • Shapes & Line ใช้วาดรูปหรือเส้น
    • Icon มีไอคอนให้เลือกใช้
    • Photo มีรูปภาพให้เลือกใช้
    • Photo Frame มีกรอบรูปให้เลือกใช้
    Uploads

    • โปรแกรมมีรูปให้เลือกใช้
    • หรือสามารถอัพโหลดรูปจากภายนอกเข้ามาใช้งานได้
    Background

    • มีรูปพื้นหลังให้เลือกใช้งานในการตกแต่ง
    • สามารถปรับความเข้มได้ที่ Opacity
    Text

    • สามารถเลือกรูปแบบตัวอักษร
    • สามารถเลือกปรับแต่ง Style กรอบรูปหรืออื่นๆ ได้ตามด้านล่าง
    Tools

    • สามารถเลือกรูปแบบ Charts ทำกราฟได้
    • เลือก Maps ทำแผนที่ได้
    • หรือสามารถแทรก link videos ได้
    • Saved          บันทึก
    • Preview       ดูผลลัพธ์
    • Download    แปลงไฟล์เป็น JPEG หรือ PNG
    • Share          เผยแพร่ผลงานสู่สาธารณะ

     

    แหล่งความรู้อ้างอิง

  • วิธีเขียน web service ตรวจสอบไฟล์ใน url ที่ส่งมาว่ามีไฟล์หรือไม่

    เนื่องจากผู้เขียนได้มีการทำงานที่ต้องตรวจสอบไฟล์ จึงอยากจะแชร์ประสบการณ์การเขียน web service โดยยกตัวอย่างการเขียนดังนี้

    โดยจากตัวอย่างผู้เขียนได้ใช้ฟังก์ชัน client.DownloadData(url) โดยผลลัพธ์ที่ได้ มีดังนี้

    • ถ้าไม่มีไฟล์อยู่ก็จะตอบกลับมาเป็น “File not found.”
    • ถ้ามีไฟล์ระบบจะตอบกลับมาว่ามีจริง

    ซึ่งหวังว่าจะมีประโยชน์ไม่มากก็น้อยค่ะ

     

  • Adaptive Layout สำหรับแอปพลิเคชันบนระบบปฏิบัติการ iOS

    สำหรับหัวข้อนี้สามารถนำไปใช้ได้ทั้งกับนักพัฒนาที่ใช้ Xcode และ Xamarin.iOS นะครับ แต่ภาพตัวอย่างที่ใช้ประกอบในบทความจะมาจาก Xamarin.iOS บน Visual Studio ครับ

    อุปกรณ์ที่ใช้งานระบบปฏิบัติการ iOS ในปัจจุบัน มีอะไรบ้าง และขนาดหน้าจอ ความละเอียดเท่าไหร่  คงเป็นคำถามแรกๆสำหรับนักพัฒนาแอปพลิเคชันสำหรับใช้งานบนระบบปฏิบัติการ iOS ก่อนที่จะเริ่มออกแบบหน้าจอ

    ข้อมูลจาก http://iosres.com/

    นี้คือคำตอบนั้นครับ ความหลากหลายของขนาดหน้าจอ  จะเห็นได้ว่าเยอะไม่แพ้ Android เลยทีเดียวสำหรับปัจจุบัน

    เมื่อแอปพลิเคชันของเราจำเป็นต้องทำงานได้บนทุกอุปกรณ์  ต้องทำอย่างไร บทความนี้จะสรุปสิ่งที่ต้องรู้และศึกษาเพิ่มเติม เกี่ยวกับการทำ Adaptive Layout หรือ รูปแบบการแสดงผลที่ปรับเปลี่ยนไปตามขนาดหน้าจอได้เอง

     

    Unified Storyboard

    เล่าวิวัฒนาการของวิธีการออกแบบ UI ของ iOS แอปพลิเคชันซักหน่อยนะครับ โดยแต่เริ่มนั้นเนื่องจากมีเพียงแค่ iPhone ที่ใช้ระบบปฏิบัติการ iOS เครื่องมือของทาง Apple สำหรับใช้ออกแบบหน้าจอเรียกว่า Interface Builder ซึ่งปัจจุบันถูกผนวกรวมมากับ Xcode เรียบร้อยแล้ว ใช้ไฟล์ .xib ในการออกแบบ ลักษณะจะเป็น 1 หน้าจอ 1 ไฟล์  ใช้ไฟล์เพียงชุดเดียว แต่เมื่อมี iPad ซึ่งมีขนาดหน้าจอที่แตกต่างออกไป นักพัฒนาก็ต้องสร้างไฟล์สำหรับ iPad อีกชุด

    ต่อมาไฟล์สำหรับออกแบบ UI ที่ชื่อว่า Storyboard  ก็ถูกนำมาใช้งาน เป็นการออกแบบในลักษณะที่ สามารถวางหน้าจอ หลายๆ หน้าจอ และกำหนดความเชื่อมโยง โดยใช้ segue เป็นตัวเชื่อมการแสดงผล แต่ก็ยังต้องมี ไฟล์ Storyboard สำหรับ iPhone และ iPad แยกกันอยู่ดี

    เมื่อมาถึง iOS 8.0 ความหลากหลายของขนาดหน้าจอมีมากขึ้นแม้แต่ iPhone เอง ก็มีหลายขนาด ตัว Unified Storyboard จึงถูกนำมาใช้เพื่อแก้ปัญหานี้ ทำให้ออกแบบ Storyboard เพียงไฟล์เดียวสามารถใช้งานได้กับอุปกรณ์ทุกขนาดหน้าจอ โดยใช้ร่วมกับเทคโนโลยีอีก 2 อย่างคือ Auto Layout และ Size Class

    วิธีการใช้งาน

    1. เมื่อสร้างโปรเจ็คจะมีไฟล์ .storyboard เปิดไฟล์ จากนั้นในหน้าต่าง Property เลือกใช้งาน Use Auto Layout, Use Size Classes

    2. ด้านบนของ Interface Builder จะปรากฏเมนูที่เกี่ยวกับการทำ Unified Storyboard ดังรูป

    3. ทำการกำหนดค่าใน info.plist เพื่อใช้ไฟล์ Storybord ดังกล่าวเป็น Main interface ของทั้ง iPhone และ iPad

    4. ตอนนี้ไฟล์ Storyboard ของเราก็พร้อมใช้งาน รองรับการออกแบบโดยมีความสามารถ Auto Layout และ Size Class ให้ใช้งานแล้วครับ

     

    Auto Layout

    แนวคิดหลักของ Auto Layout คือการตั้งเงื่อนไขเพื่อกำหนดตำแหน่งและขนาดของ Control ที่อยู่บนหน้าจอ เพื่อให้สามารถปรับตำแหน่งให้เหมาะสมกับขนาดหน้าจอที่เปลี่ยนไปได้ ซึ่งเรียกว่า Constraint มีหลายชนิดด้วยกันดังนี้

    1. Size Constraints คือการกำหนดขนาด โดยระบุ ความกว้าง ความสูง โดยส่วนตัวผมคิดว่าแบบนี้ค่อนข้างได้ใช้น้อยครับ เพราะเหมือนกับเรากำหนด Property ความกว้าง ความสูง ปกติ เหมาะใช้กำหนดแค่ความกว้าง หรือ ความสูงอย่างใดอย่างหนึ่งผสมกับ Constraints แบบอื่นๆ
    2. Center Constraints คือการกำหนดให้อยู่ในจุดกึ่งกลาง โดยอ้างอิงจากขนาดของ View ที่เปลี่ยนไป จะทำให้อยู่ในตำแหน่งกลางเสมอ อันนี้ก็ได้ใช้งานบ่อยครับ
    3. Combinational Constraints คือการอ้างอิงตำแหน่งของ Control จาก เส้นขอบทั้ง 4 ด้าน และ Control อื่นๆ ที่อยู่ใน View หรือแม้กระทั่ง Toolbar, Tab bar, Header, Footer ของ View อันนี้ผมใช้เยอะสุดครับ

    วิธีการใช้งาน

    1. เมื่อได้เปิดใช้งาน Auto Layout ในขั้นตอนการสร้าง Unified Storyboard แล้วนั้น จะปรากฏ Toolbar เมื่อต้องการใช้งาน ให้คลิกเลือกที่ Control ที่ต้องการ จากนั้นคลิกที่ไอคอน เพิ่ม ลบ หรืออัพเดทเฟรม ตามลำดับ โดยที่การเพิ่ม Constraint โดยวิธีการนี้จะเพิ่ม 2 ตำแหน่ง คือ top/bottom + left/right หาก Control นั้นไม่ได้มี intrinsic size (Control ที่จำกัดขนาดกับ View ที่แสดงผล นักพัฒนาไม่สามารถแก้ไขได้) ก็จะเพิ่ม Constraint ระบุ width + height ให้ด้วย

    2. จะเห็นได้ว่าการเพิ่ม Constraint โดยใช้ Toolbar นั้น ไม่สามารถตอบสนองการใช้งานได้ครบถ้วน เราสามารถเพิ่ม Constraint ทีละตำแหน่งได้เอง โดยดับเบิ้ลคลิกที่ Control ให้มีลักษณะ ดังรูป

    3. จากนั้นสามารถคลิกแล้วลาก ไปยังตำแหน่งอ้างอิง ซึ่งจะเปลี่ยนไปตามแต่ละด้านของ Control

    4. เมื่อเพิ่มสำเร็จสามารถตรวจสอบดูได้ที่ Property > Layout

    5. ในขณะที่เราเพิ่ม Constraint นั้น อาจจะสังเกตุได้ว่า กรอบของ Control เป็นสีส้ม ซึ่งหมายถึง Constraint ที่เรากำหนดไปนั้นยังไม่สมบูรณ์ หรือมีความขัดแย้งกัน ทำให้ไม่สามารถแสดงผลได้ถูกต้อง เรียกว่า Error นั้นเอง แนะนำว่าให้เพิ่มจนครบทุกด้าน และให้มี Constraint  เพียงชนิดเดียวในแต่ละด้านครับ ถ้ายัง Error อยู่ ให้ลองเช็คจุดอ้างอิง ถ้าเป็น Control ด้วยกัน Control ที่ใช้อ้างอิงจะต้องไม่ Error ครับ ไม่เช่นนั้นจะพากัน Error ทั้งหมด

    6. หากเพิ่ม Constraint สมบูรณ์กรอบจะเป็นสีน้ำเงิน แบบนี้ครับ และจะมีเส้นไกด์สีน้ำเงินเล็กๆ บอกตำแหน่งอ้างอิงอยู่ด้วย

    7. ทำการทดสอบแอปพลิเคชัน ด้วย Simulator ที่ขนาดหน้าจอต่างๆ ว่าแสดงผลถูกต้องหรือไม่

     

    Size Class

    เพื่อให้การออกแบบโดยใช้เพียงไฟล์ Storyboard เดียวใช้งานได้กับหน้าจอทุกขนาด ยังมีปัญหาในเรื่องของ การแสดงผลในแนวตั้ง และแนวนอน ที่การกำหนด Constraint ตอนทำ Auto Layout ไม่สามารถทำให้แสดงผลได้ถูกต้องซะทีเดียว จำเป็นต้องมีการแบ่งประเภทหน้าจอแสดงผล โดยใช้ Grid ขนาด 3×3 ซึ่งจะจำแนกได้เป็น 9 แบบ ดังรูป

    โดยจะมีคำเรียกขนาด 3 ชนิด คือ Compact, Any, Regular จากเล็กไปใหญ่ตามลำดับ  เมื่อนำเอา ความกว้าง และ ความสูง ทั้ง 3 แบบมาใช้ร่วมกันก็จะเป็นตัวแทนของ หน้าจอแบบต่างๆของทุกอุปกรณ์ที่ใช้ระบบปฏิบัติการ iOS ดังนี้ครับ

    ภาพประกอบจาก https://developer.apple.com/reference/uikit/uitraitcollection

    สังเกตุว่าจะมี แค่ 6 ขนาดที่เทียบกับอุปกรณ์จริงๆได้ ส่วนอีก 3 ขนาด ที่มีความกว้าง ความสูง แบบ Any ผสมอยู่ก็เพื่อใช้กับการออกแบบที่ไม่จำเป็นต้องระบุ Size Class เพราะสามารถใช้ Constraint ร่วมกันได้นั้นเองครับ

    วิธีการใช้งาน

    1. เมื่อต้องการติดตั้ง Size Class ใดๆเพิ่มเติม ให้เลือก Control ที่ต้องการจากนั้นในหน้าต่าง Property เลื่อนลงไปที่ Stretching คลิกที่รูปไขควง

    2. จะมีตัวเลือก Compact, Any, Regular ให้เลือก 2 ลำดับ โดยตัวเลือกลำดับแรกคือชนิดความกว้าง ลำดับที่ 2 คือชนิดความสูง

    3. เลือก Size Class ที่ต้องการออกแบบ จาก Toolbar

     

    ในบทความนี้เป็นเพียงแนวทาง และหยิบประเด็นใหญ่ๆที่ควรทราบในเรื่องของการทำ Adaptive Layout มาเท่านั้น เมื่อท่านใช้งานจริงจะยังมีประเด็นต่างๆที่ต้องศึกษาเพิ่มเติมอีกมากมาย หวังว่าสิ่งที่ผมรวบรวมมาจะเป็นประโยชน์ไม่มากก็น้อยครับ

    แหล่งอ้างอิง

    https://developer.xamarin.com/guides/ios/platform_features/introduction_to_Unified_Storyboards/

    https://developer.xamarin.com/guides/ios/user_interface/designer/designer_auto_layout/

  • วิธีการ set property ของ radio button ใน Dojo

    เนื่องจากช่วงนี้ ผู้เขียนมีโอกาสได้เข้ามาอยู่ในแวดวงของ Dojo และได้ประสบกับปัญหาในการที่จะ set property ของ Dojo ซึ่งในที่นี้คือ Radio Button หลังจากที่ได้ลองผิด ลองถูก Error กันหัวหมุน  จนสุดท้ายได้เจอทางออก  เลยอยากจะบันทึกไว้สำหรับตัวเองมาดูในอนาคต และเผื่อท่านอื่นที่ประสบปัญหาเดียวกัน มาเจอจะได้ลองนำไปใช้งานกันดูค่ะ Let’s GO!!!

     

    เนื่องจาก Radio Button เป็น Control ภายใต้ dijit/form/RadioButton ดังนั้น การเขียนคำสั่งเพื่อ set property จึงได้เป็นดังนี้

    dijit.byId(‘control_id‘).set(‘control_prop‘, value);

    control_id : id ของ control นั้น ๆ

    control_prop : property ของ control ที่ต้องการกำหนดค่า

    value : ค่าที่ต้องการกำหนด

     

    ตัวอย่างเช่น ต้องการกำหนดให้ radio button ที่มี id=”rdBtn1″ ไม่สามารถใช้งานแต่ยังแสดง(disable) และ

    id=”rdBtn2″ มีค่าโดยปริยายเป็นเลือกไว้ จะเขียน Code ได้ดังนี้

    dijit.byId(‘rdBtn1’).set(‘disabled’, true); 

    dijit.byId(‘rdBtn2’).set(‘checked’, true); 

     

    ซึ่ง properties ของ control ต่าง ๆ สามารถดูเพิ่มเติมได้จากเว็ปเพจของ Dojo ตามลิงค์นี้ค่ะ Dojo Documentation

    หวังว่าบทความนี้จะเป็นประโยชน์แก่ท่านผู้อ่านไม่มาก็น้อยนะคะ แล้วพบกันใหม่ตอนหน้าค่ะ